11

私はこれを行うための明確な方法を探していましたが、例を挙げてそれを非常によく説明する場所を見つけられませんでした. あなたが私を助けてくれることを願っています。

私が使用しているコードは次のとおりです。

- (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;

NSCache *cache = [_cachedImages objectAtIndex:indexPath.row];

[cache setName:@"image"];
[cache setCountLimit:50];

UIImage *currentImage = [cache objectForKey:@"image"];

if (currentImage) {
    NSLog(@"Cached Image Found");
    cell.imageView.image = currentImage;
}else {
    NSLog(@"No Cached Image");

    cell.newsImage.image = nil;

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void)
                   {
                       NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:item.image]];
                       dispatch_async(dispatch_get_main_queue(), ^(void)
                       {
                           cell.newsImage.image = [UIImage imageWithData:imageData];
                           [cache setValue:[UIImage imageWithData:imageData] forKey:@"image"];
                           NSLog(@"Record String = %@",[cache objectForKey:@"image"]);
                       });
                   });
}

return cell;
}

キャッシュは nil を返します。

4

2 に答える 2

15

Nitin は、キャッシュの使用方法に関する質問に非常にうまく答えました。問題は、元の質問と Nitin の回答の両方が、GCD を使用しているという問題に悩まされていることです。これは、(a) 同時要求の数を制御できません。(b) ディスパッチされたブロックはキャンセルできません。さらに、dataWithContentsOfURLキャンセルできない を使用しています。

WWDC 2012 のビデオAsynchronous Design Patterns with Blocks, GCD, and XPC、セクション 7、「Separate control and data flow」、ビデオの約 48 分を参照して、これが問題になる理由、つまりユーザーがリストをすばやく下にスクロールした場合について説明します。 100 番目の項目まで、他の 99 の要求はすべてキューに入れられます。極端な場合には、使用可能なすべてのワーカー スレッドを使い果たす可能性があります。また、iOS では 5 つの同時ネットワーク リクエストしか許可されていないため、これらのスレッドをすべて使い切っても意味がありません (また、ディスパッチされたブロックの一部が、5 つ以上のリクエストがあるために開始できないリクエストを開始した場合、ネットワーク リクエストの一部が失敗し始めます)。 )。

そのため、ネットワーク リクエストを非同期で実行し、キャッシュを使用する現在のアプローチに加えて、次のことを行う必要があります。

  1. 操作キューを使用すると、(a) 同時リクエストの数を制限できます。(b) 操作をキャンセルする機能を開きます。

  2. キャンセル可能NSURLSessionも使用してください。これは自分で行うことも、AFNetworking や SDWebImage などのライブラリを使用することもできます。

  3. セルが再利用される場合、前のセルに対する保留中の要求 (ある場合) をキャンセルします。

これは実行可能であり、適切に実行する方法を示すことができますが、コードが多くなります。最良のアプローチは、UIImageViewキャ​​ッシュを行うだけでなく、これらの他のすべての問題も処理する多くのカテゴリの 1 つを使用することです。SDWebImageUIImageViewカテゴリはかなり良いです。また、コードが大幅に簡素化されます。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellIdentifier = @"NewsCell";    // BTW, stay with your standard single cellIdentifier

    NewsCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier indexPath:indexPath];

    NewsItem *item = newsItemsArray[indexPath.row];

    cell.newsTitle.text = item.title;

    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:item.image]
                      placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

    return cell;
}
于 2013-10-11T21:22:20.460 に答える