在可重用表格单元格中使用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; } 

caching返回为零。

Nitin回答了如何使用caching相当好的问题。 问题是,最初的问题和Nitin的答案都有一个问题,就是你正在使用GCD,它(a)不能控制并发请求的数量; 和(b)被派遣的块不可取消。 此外,你正在使用dataWithContentsOfURL ,这是不可取消。

请参阅WWDC 2012video带有块,GCD和XPC的asynchronousdevise模式 ,第7节“分离控制和数据stream”,约48分钟进入video,讨论为何存在问题,即如果用户快速向下滚动列表到第100个项目,所有其他99个请求都将排队。 在极端的情况下,可以使用所有可用的工作线程。 而iOS只允许五个并发的networking请求,所以没有必要使用所有这些线程(并且如果一些调度的块启动了由于超过五个而不能启动的请求,一些networking请求将开始失败)。

因此,除了当前使用cachingasynchronous执行networking请求的方法之外,还应该:

  1. 使用操作队列,它允许您(a)约束并发请求的数量; (b)开放取消操作的能力;

  2. 也使用可取消的NSURLSession 。 你可以自己做或者使用像AFNetworking或SDWebImage这样的库。

  3. 当单元格被重用时,取消前一个单元格的任何未决请求(如果有的话)。

这可以做到,我们可以告诉你如何正确地做到这一点,但它是很多的代码。 最好的方法是使用caching的许多UIImageView类别之一,但也处理所有这些其他问题。 SDWebImage的UIImageView类别相当不错。 这大大简化了你的代码:

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

可能是你做了一些错误的设置NSCache每个图像相同的密钥

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

使用这个而不是上面设置的ForKey作为一个图像pathitem.image和使用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; }