0

xml から画像を取得するために非同期を使用しています。パーサーは正しく機能しており、URL を出力できます。非同期で、画像を変更可能な辞書にキャッシュしようとしています。ループに陥ってしまい、画像がまったく出力されなくなりました。これが私が立ち往生している私のコードです。

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {

        NSLog(@"Got Here");

        static NSString *CellIdentifier = @"NewsCell";
        NewsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

        NSLog(@"Got Here 2");

        // Configure the cell...

        NewsItem *item = [newsItemsArray objectAtIndex:indexPath.row];

        cell.newsTitle.text = item.title;

        NSLog(@"Got Here 3");

        NSMutableDictionary *record = [_records objectAtIndex:[indexPath row]];
        if ([record valueForKey:@"actualImage"]) {
            NSLog(@"Record Found");
            [cell.newsImage setImage:[record valueForKey:@"actualImage"]];
        } else {
            NSLog(@"Record Not Found");
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,(unsigned long)NULL), ^(void)
                   {
                       NSLog(item.image);
                       NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:item.image]];
                       dispatch_async(dispatch_get_main_queue(), ^(void){
                           [record setValue:[UIImage imageWithData:imageData] forKey:@"actualImage"];
                           [cell.newsImage setImage:[record valueForKey:@"actualImage"]];
                           if (tableView) {
                               [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
                           }
                       });
                   });
        }

        return cell;
    }

よろしくお願いします。

4

3 に答える 3

0

UITableViewCellサブクラスを作成します。イメージをロードする必要がある場合NSOperationは、実際のネットワーク接続を行うサブクラスを作成します。(例については、 http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/を参照してください)。または、これを処理するサードパーティのパッケージを使用してください。何百万ものパッケージがあります。私の現在のお気に入りは MKNetworkKit です。操作への参照を保存します。セルのprepareForReuseで、接続が完了していない場合は接続をキャンセルします。これにより、最初のリクエストが2番目のリクエストの後に完了した場合に、スクロール時にセルが間違った画像を取得するのを防ぐことができます(思ったよりも頻繁に発生します)。

于 2013-10-10T18:42:56.553 に答える
0

次のコードを試すことができます。また、問題を調査し、他の既存の回答と説明を見つけようとする必要があります。これは非常によくある質問で、SO で無数に回答されています ;)

例:ブロックを使用してテーブルに画像をロードする

コードは最も明白な問題を修正しようとしますが、すべてのエッジ ケースで機能するという保証はありません。デバッグしてテストする必要があります。

newsImage元のコードの主な問題は、完了ブロック内でセルのプロパティを設定しようとすることです。それが実行されると、キャプチャされたセルは同じ行に関連付けられなくなる可能性があります。覚えておいてください:細胞は再利用されます!

したがって、完了ブロックでは、変更されたコードは、ブロックの開始時にキャプチャされた indexPath から、対応するセルを再評価します。

NewsCell* cell2 = [tableView cellForRowAtIndexPath:indexPath]; // cell equals nil, if not visible

返されたセルが表示されない場合があり、その場合、テーブル ビューは nil を返します。

基本的にレコード変数も同様です。理由はさまざまです。レコード変数は自動スコープを持つ変数です。つまり、メソッドが戻ると解放されます。ブロックでキャプチャすることは、おそらく良い考えではありません。

したがって、ブロックで再評価する必要があります。それは基本的に[self.records objectAtIndex:[indexPath row]];

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"NewsCell";

    NewsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    // Configure the cell...

    NewsItem *item = [newsItemsArray objectAtIndex:indexPath.row];
    cell.newsTitle.text = item.title;
    NSMutableDictionary *record = [_records objectAtIndex:[indexPath row]];
    if ([record valueForKey:@"actualImage"]) {
        NSLog(@"Record Found");
        [cell.newsImage setImage:[record valueForKey:@"actualImage"]];
    } else {
        NSLog(@"Record Not Found");
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,(unsigned long)NULL), ^(void)
               {
                   NSLog(item.image);
                   NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:item.image]];
                   dispatch_async(dispatch_get_main_queue(), ^(void){
                       NSMutableDictionary *record2 = [_records objectAtIndex:[indexPath row]];
                       [record2 setValue:[UIImage imageWithData:imageData] 
                                                                   forKey:@"actualImage"];
                       NewsCell* cell2 = [tableView cellForRowAtIndexPath:indexPath]; // cell equals nil, if not visible
                       if (cell2) {
                           [cell2.newsImage setImage:[record2 valueForKey:@"actualImage"]];
                           [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
                        }
                   });
               });
    }

    return cell;
}
于 2013-10-10T18:19:28.360 に答える
0

まずは交換

[record setValue:[UIImage imageWithData:imageData] forKey:@"actualImage"];

と

[record setObject:[UIImage imageWithData:imageData] forKey:@"actualImage"];

そしてあなたのコードはうまくいくはずです。辞書は常に空であるため、常に else ブロックに移動します。NSMutableDictionary を印刷すると、これに気付くでしょう。

これはキャッシングが行われる方法ではありません。それは記憶だけでなく、コーディングの実践についてもです。

NSCache やSDWebImageなどのクリーンなアプローチを使用してください

SDWebImage はキャッシュも処理します。GCD ではなく NSOperation を使用してリクエストを行います。ネストされたブロックと同時発生の問題を回避します。きれいなコードは次のようになります

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"セル";
    NewsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    NewsItem *item = [newsItemsArray objectAtIndex:indexPath.row];

    cell.newsTitle.text = item.title;
     // ライブラリのカテゴリ
    [cell.imageView setImageWithURL:newsItem.imageURL];

    if ([item needsRefresh] )
    {
         cell.newsTitle.text = item.title;

        NewsOperation *operation = [[NewsOperation alloc] initForNewsItem:newItem 完了:^(NewsItem *newsItem, NSError *error) {
            if (ニュース項目)
            {
                [tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
            }
        }];
        [[self newsItemQueue] addOperation:操作];
    }

    セルを返します。
}

于 2013-10-10T18:48:01.153 に答える