5

デスクトップアプリ(iPhoneアプリケーションではありません!!)の非同期NSURLConnectionから返されたデータをNSImageに取り込む方法を理解しようとして、ことわざの壁にぶつかりました。

これが状況です。

カスタムセルを使用しているテーブルがあります。各カスタムセルには、WebサーバーからプルされているNSImageがあります。画像にデータを入力するために、同期リクエストを簡単に実行できます。

myThumbnail = [[NSImage alloc] initWithContentsOfFile:myFilePath];

これに伴う問題は、画像が入力されるまでテーブルがブロックされることです(明らかに同期要求であるため)。大きなテーブルでは、これによりスクロールが耐えられなくなりますが、画像のサイズが大きい場合は、最初の実行で画像を入力するだけでも面倒な場合があります。

そこで、 Appleのドキュメントに従って、独自のスレッドでデータを取得する非同期リクエストクラスを作成します。問題ありません。データがプルされ、データが入力されているのを(ログファイルを介して)確認できます。

私が抱えている問題は、データを取得したら、呼び出し元のクラス(カスタムテーブルビュー)へのコールバックが必要になることです。

私はこのようなことができるという印象を受けましたが、(私が想定しているように)私の呼び出しクラスが本当に必要としているのはデリゲートであるため、機能しません。

NSImage * myIMage;
myImage = [myConnectionClass getMyImageMethod];

接続クラスのデリゲートでは、データを取得していることがわかりますが、呼び出し元のクラスにデータを戻す方法がわかりません。私のconnectionDidFinishLoading方法はAppleドキュメントから直接です:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    // do something with the data
    // receivedData is declared as a method instance elsewhere
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);

    // release the connection, and the data object
    [connection release];
    [receivedData release];
}

これが解決するのが簡単な問題であることを願っていますが、私はこれについての知識の限界に達しているのではないかと心配しています。

最終的には、アプリの高度なキャッシュメカニズムを使用して、テーブルビューでローカルマシンの画像をチェックしてからサーバーから画像を取得し、画像が取得されるまで進行状況インジケーターを表示するようになります。現在、同期プロセスを使用して画像が十分に大きい場合、ローカル画像の作成でさえも遅くなる可能性があります。

ありとあらゆる助けをいただければ幸いです。

ソリューションの更新

ベンの助けのおかげで他の誰かが同様の解決策を必要とする場合に備えて、ここで私が思いついたものです(もちろん一般的に投稿用に変更されています)。また、画像のカスタムキャッシュを実装し、画像を呼び出すためにアプリのさまざまな場所で使用できるように、画像読み込みクラスを汎用的にしたことを覚えておいてください。

私の呼び出しメソッドでは、私の場合はテーブル内のカスタムセルでした...

ImageLoaderClass * myLoader = [[[ImageLoaderClass alloc] init] autorelease];

    [myLoader fetchImageWithURL:@"/my/thumbnail/path/with/filename.png"
                     forMethod:@"myUniqueRef"
                        withId:1234
                   saveToCache:YES
                     cachePath:@"/path/to/my/custom/cache"];

これにより、myLoaderクラスのインスタンスが作成され、4つのパラメーターが渡されます。取得する画像のURL、通知オブザーバーを設定するときに呼び出しを行ったクラスを決定するために使用する一意の参照、画像のID、画像をキャッシュに保存するかどうか、パスキャッシュに。

私のImageLoaderClassは、呼び出し元のセルから渡されるものを設定する上記のメソッドを定義します。

-(void)fetchImageWithURL:(NSString *)imageURL 
           forMethod:(NSString *)methodPassed 
              withId:(int)imageIdPassed 
         saveToCache:(BOOL)shouldISaveThis
           cachePath:(NSString *)cachePathToUse
{

    NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]
                                          cachePolicy:NSURLRequestUseProtocolCachePolicy
                                      timeoutInterval:60.0];

    // Create the connection with the request and start loading the data

    NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];

    if (theConnection) {

        // Create the NSMutableData that will hold
        // the received data 
        // receivedData is declared as a method instance elsewhere

        receivedData = [[NSMutableData data] retain];

        // Now set the variables from the calling class

        [self setCallingMethod:methodPassed];
        [self setImageId:imageIdPassed];
        [self setSaveImage:shouldISaveThis];
        [self setImageCachePath:cachePathToUse];

    } else {
        // Do something to tell the user the image could not be downloaded
    }
}

このconnectionDidFinishLoading方法では、必要に応じてファイルをキャッシュに保存し、リスニングオブザーバーに通知呼び出しを行いました。

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);

    // Create an image representation to use if not saving to cache
    // And create a dictionary to send with the notification    

    NSImage * mImage = [[NSImage alloc ] initWithData:receivedData];
    NSMutableDictionary * mDict = [[NSMutableDictionary alloc] init];

    // Add the ID into the dictionary so we can reference it if needed

    [mDict setObject:[NSNumber numberWithInteger:imageId] forKey:@"imageId"];

    if (saveImage)
    {
        // We just need to add the image to the dictionary and return it
        // because we aren't saving it to the custom cache

        // Put the mutable data into NSData so we can write it out

        NSData * dataToSave = [[NSData alloc] initWithData:receivedData];

        if (![dataToSave writeToFile:imageCachePath atomically:NO])
    NSLog(@"An error occured writing out the file");
    }
    else
    {
        // Save the image to the custom cache

        [mDict setObject:mImage forKey:@"image"];
    }

    // Now send the notification with the dictionary

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    [nc postNotificationName:callingMethod object:self userInfo:mDict];

    // And do some memory management cleanup

    [mImage release];
    [mDict release];
    [connection release];
    [receivedData release];
}

最後に、テーブルコントローラーでオブザーバーを設定して通知をリッスンし、カスタムセルの再表示を処理するメソッドに送信します。

-(id)init
{
    [super init];

    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    [nc addObserver:self selector:@selector(updateCellData:) name:@"myUniqueRef" object:nil];

    return self;
}

問題が解決しました!

4

3 に答える 3

3

私の解決策は、この目的のためにGrand Central Dispatch(GCD)を使用することです。サーバーから画像を取得した後、画像をディスクに保存することもできます。

- (NSView *)tableView:(NSTableView *)_tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
    SomeItem *item = [self.items objectAtIndex:row];
    NSTableCellView *cell = [_tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
    if (item.artworkUrl)
    {
        cell.imageView.image = nil;
        dispatch_async(dispatch_queue_create("getAsynchronIconsGDQueue", NULL), 
        ^{
            NSURL *url = [NSURL URLWithString:item.artworkUrl];
            NSImage *image = [[NSImage alloc] initWithContentsOfURL:url];
            cell.imageView.image = image;        
        });
    }
    else
    {
        cell.imageView.image = nil;    
    }
    return cell;
}

(私は自動参照カウント(ARC)を使用しているため、保持と解放はありません。)

于 2011-11-08T21:08:41.863 に答える
1

あなたの直感は正しいです。NSURLConnectionのデリゲートであるオブジェクトから、テーブルビューを管理するコントローラーへのコールバックが必要です。これにより、データソースが更新-setNeedsDisplayInRect: され、画像が対応する行のrectで呼び出されます。

于 2009-11-16T18:51:16.383 に答える
0

メソッドを使用してみましたinitWithContentsOfURL:か?

于 2009-11-16T05:27:35.043 に答える