Iphone 在可重用的表单元中使用NSCache和dispatch\u async的正确方法是什么?
我一直在寻找一个明确的方法来做到这一点,但没有找到任何地方可以给出一个例子,并解释得很好。我希望你能帮助我 以下是我正在使用的代码:Iphone 在可重用的表单元中使用NSCache和dispatch\u async的正确方法是什么?,iphone,ios,asynchronous,nscache,Iphone,Ios,Asynchronous,Nscache,我一直在寻找一个明确的方法来做到这一点,但没有找到任何地方可以给出一个例子,并解释得很好。我希望你能帮助我 以下是我正在使用的代码: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"NewsCell"; NewsCell *cell = [tableView de
- (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;
}
缓存为我返回零。可能是您做错了,您为
NSCache
[cache setValue:[UIImage imageWithData:imageData] forKey:@"image"];
使用此选项而不是上面的set-ForKey作为Imagepath项。image并使用setObject而不是setVlaue:-
[self.imageCache setObject:image forKey:item.image];
请尝试以下代码示例:-
in.h类:-
@property (nonatomic, strong) NSCache *imageCache;
- (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;
}
in.m类:-
@property (nonatomic, strong) NSCache *imageCache;
- (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;
}
尼廷回答了关于如何很好地使用缓存的问题。问题是,原始问题和Nitin的答案都存在一个问题,即使用GCD时,(a)不能控制并发请求的数量;和(b)调度的块是不可取消的。此外,您正在使用
dataWithContentsOfURL
,这是不可取消的
请参阅WWDC 2012视频,第7节,“单独的控制和数据流”,视频开始约48分钟,了解这一问题的原因,即如果用户快速向下滚动列表至第100项,所有其他99个请求都将排队。在极端情况下,您可能会耗尽所有可用的辅助线程。而且iOS只允许五个并发网络请求,因此没有必要用尽所有线程(如果一些已调度的块启动由于超过五个而无法启动的请求,则您的一些网络请求将开始失败)
因此,除了当前异步执行网络请求和使用缓存的方法外,您还应该:
nsursession
。您可以自己完成,也可以使用AFNetworking或SDWebImage之类的库UIImageView
类别中的一个,它们进行缓存,但也处理所有其他问题。的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;
}
如果在加载图像时重复使用单元格,会发生什么情况?您似乎在单元格中设置了错误的映像。是的,我还希望缓存objectAtIndex:indexath.row,以便映像与表行一样属于缓存中的相应插槽。现在,当您在加载时滚动图片时,它会将错误的图片加载到错误的行中。我希望答案能反映这一点,因为我使用的是可重复使用的细胞。谢谢你的回答。@Kendallhlhelmstettergelner因为Nitin聪明地在最后的
dispatch\u async
中调用了cellforrowatinexpath
,它确保了当你将单元格重新用于表的另一行时,旧的调度任务不会不适当地更新错误的行。因此,这个实现优雅地让旧的请求完成,并为下一个单元将新任务分派到后台。这是一个很好的实现(尽管在连接速度慢或图像较大的情况下,一个稍微更优雅的解决方案是取消旧请求或以某种方式延迟它们)。尽管我同意您可能希望在某个时候添加取消请求的选项。@Nitin Gohel感谢您的回答。我无法获得对我的代码所做的更改(根据您的回答),这导致了令人满意的应用程序更改,这无疑是我的错。不过,我会选择Rob的答案作为我问题的最佳解决方案和答案。你可能想看看SDWebImage项目,它已经解决了所有问题。你能提供一个链接吗?非常好的解释,我也学到了正确的东西,因为它给了我们非常好的答案。事实上,我只是根据NSCache的问题给出了答案。但是是的,Sdwebimage和AFnetworking也非常适合这样做task@Rob有没有比我如何提问更好的方法来更好地理解你的答案。谢谢你的回答,我觉得它很有用。@Cedric不,我认为你的问题很好。你问了“我如何缓存”,你提供了足够的上下文让我们不喊“提供代码!”或“你尝试了什么?!”。哈哈,只是在这种情况下,应该使用GCD的假设并不是最优的。这是苹果建议使用操作队列的案例之一。而UIImageView
类别只不过更进一步,让您远离基于NSOperation
的代码的杂草。但是,如果在我的回答中有什么你不明白的地方(尤其是在看了视频之后),请告诉我们。