Каков правильный способ использования NSCache с dispatch_async в многоразовой ячейке таблицы?

Я искал четкий способ сделать это и нигде не нашел, что даст пример и объяснит это очень хорошо. Надеюсь, вы можете мне помочь.

Вот мой код, который я использую:

- (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 для меня.

Ответ 1

Нитин ответил на вопрос, как правильно использовать кеш. Дело в том, что и исходный вопрос, и ответ Nitin страдают от проблемы, которую вы используете GCD, которая (a) не обеспечивает контроль над количеством параллельных запросов; и (б) отправленные блоки не могут быть отменены. Кроме того, вы используете dataWithContentsOfURL, который не отменяется.

Смотрите видео WWDC 2012 Асинхронные шаблоны проектирования с блоками, GCD и XPC, раздел 7, "Отдельный контроль и поток данных", о 48 минут в видео для обсуждения того, почему это проблематично, а именно, если пользователь быстро прокручивает список до 100-го элемента, все остальные 99 запросов будут помещены в очередь. В крайних случаях вы можете использовать все доступные рабочие потоки. И iOS разрешает только пять одновременных сетевых запросов, поэтому нет смысла использовать все эти потоки (и если некоторые отправленные блоки запускают запросы, которые не могут запускаться, потому что их больше, чем пять, некоторые из ваших сетевых запросов начнут сбой).

Итак, помимо вашего текущего подхода к асинхронному выполнению сетевых запросов и использованию кеша, вы должны:

  • Используйте операционную очередь, которая позволяет (а) ограничить количество одновременных запросов; и (b) открывает возможность отмены операции;

  • Используйте также отмену NSURLSession. Вы можете сделать это самостоятельно или использовать библиотеку, такую ​​как AFNetworking или SDWebImage.

  • Когда ячейка повторно используется, отмените любые ожидающие запросы (если они есть) для предыдущей ячейки.

Это можно сделать, и мы можем показать вам, как это сделать правильно, но это много кода. Лучшим подходом является использование одной из многих категорий UIImageView, которые занимаются кэшированием, но также справляются со всеми этими проблемами. Категория UIImageView SDWebImage довольно хороша. И это значительно упрощает ваш код:

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

Ответ 2

Возможно, вы ошиблись, установив одинаковый ключ для каждого изображения NSCache

[cache setValue:[UIImage imageWithData:imageData] forKey:@"image"];

Использовать это Вместо вышеопределенного параметра ForKey как элемент образа изображения и используйте setObject вместо setVlaue: -

[self.imageCache setObject:image forKey:item.image];

попробуйте этот пример кода: -

в .h Класс: -

@property (nonatomic, strong) NSCache *imageCache;

в .m классе: -

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.imageCache = [[NSCache alloc] init];

    // the rest of your viewDidLoad
}

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

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

     NewsItem *item = [newsItemsArray objectAtIndex:indexPath.row];
     cell.newsTitle.text = item.title;

    UIImage *cachedImage =   [self.imageCache objectForKey:item.image];;
    if (cachedImage)
    {
        cell.imageView.image = cachedImage;
    }
    else
    {
        cell.imageView.image = [UIImage imageNamed:@"blankthumbnail.png"];

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

               NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:item.image]];
               UIImage *image    = nil;
                if (imageData) 
                     image = [UIImage imageWithData:imageData];

                if (image)
                {

                     [self.imageCache setObject:image forKey:item.image];
                }
              dispatch_async(dispatch_get_main_queue(), ^{
                        UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
                        if (updateCell)
                           cell.imageView.image = [UIImage imageWithData:imageData];
                           NSLog(@"Record String = %@",[cache objectForKey:@"image"]);
                  });
          });            
    }
    return cell;
}