滚动从远程服务器加载大量图像的 UICollectionView 时如何避免内存问题?
How to avoid the memory issue while scrolling an UICollectionView which loads lot of images from Remote server?
我在 UICollectionView
中显示了很多图像(5000 张图像)。这些图像已从远程服务器下载。当我从 UICollectionView
的顶部滚动到底部,反之亦然 5 到 6 次时,我的应用程序崩溃并收到错误 "Terminating app due to memory pressure"。请建议解决此问题。
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return urlLists.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
UIImageView *cellImage=[[UIImageView alloc]init];
cellImage.frame=cell.contentView.bounds;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[urlLists objectAtIndex:indexPath.row]]];
UIImage *image = [UIImage imageWithData:data];
UIImageJPEGRepresentation(image, 0.1);
data=nil;
dispatch_async(dispatch_get_main_queue(), ^{
cellImage.image=image;
});
});
NSLog(@"The Indexpath=%ld", indexPath.row);
[cell.contentView addSubview:cellImage];
return cell;
}
您是否尝试过分析应用并查看其泄漏的确切位置?
这里的问题分为三级:
最严重的问题是每次使用该单元格时都会向该单元格添加一个新的 UIImageView
。但是单元格可以重复使用很多次,因此您要向单元格添加许多图像视图。与所有其他图像视图关联的图像将占用内存。
我认为 easiest/best 解决方案是使用单元格原型,在那里添加图像视图,并且不要以编程方式添加图像视图。或者,您可以通过编程方式添加图像视图,但请确保您不会每次都添加新的图像视图。
顺便说一句,不要每次都手动实例化一个新单元来关闭单元重用(正如这里其他人可能会建议的那样)。是的,这是一个简单的解决方案,但效率不高。
一个更微妙的问题是您调用 UIImageJPEGRepresentation
我猜是为了减少所涉及的内存。那并不像您认为的那样。是的,它创建了一个压缩的 JPEG 表示,但您不对其进行任何操作。你只是在丢弃它。
如果您想优化内存并且图像是高分辨率的,请考虑将图像调整为适合单元格的大小。有关调整类别大小的示例,请参阅 UIImage resizing not working properly。
考虑使用 SDWebImage or AFNetworking 的 UIImageView
类别,它可以延迟加载和缓存图像。
问题中的这段代码在启动异步检索之前不会正确重置图像视图。如果在图像检索完成之前重复使用单元格,它将不会处理这种情况。它可能会不必要地重新检索几秒钟前下载的图像。等等
有很多与优雅地异步检索图像相关的问题,这两个 类 的 UIImageView
类别为您处理了很多这些细节。
我在 UICollectionView
中显示了很多图像(5000 张图像)。这些图像已从远程服务器下载。当我从 UICollectionView
的顶部滚动到底部,反之亦然 5 到 6 次时,我的应用程序崩溃并收到错误 "Terminating app due to memory pressure"。请建议解决此问题。
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return urlLists.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
UIImageView *cellImage=[[UIImageView alloc]init];
cellImage.frame=cell.contentView.bounds;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[urlLists objectAtIndex:indexPath.row]]];
UIImage *image = [UIImage imageWithData:data];
UIImageJPEGRepresentation(image, 0.1);
data=nil;
dispatch_async(dispatch_get_main_queue(), ^{
cellImage.image=image;
});
});
NSLog(@"The Indexpath=%ld", indexPath.row);
[cell.contentView addSubview:cellImage];
return cell;
}
您是否尝试过分析应用并查看其泄漏的确切位置?
这里的问题分为三级:
最严重的问题是每次使用该单元格时都会向该单元格添加一个新的
UIImageView
。但是单元格可以重复使用很多次,因此您要向单元格添加许多图像视图。与所有其他图像视图关联的图像将占用内存。我认为 easiest/best 解决方案是使用单元格原型,在那里添加图像视图,并且不要以编程方式添加图像视图。或者,您可以通过编程方式添加图像视图,但请确保您不会每次都添加新的图像视图。
顺便说一句,不要每次都手动实例化一个新单元来关闭单元重用(正如这里其他人可能会建议的那样)。是的,这是一个简单的解决方案,但效率不高。
一个更微妙的问题是您调用
UIImageJPEGRepresentation
我猜是为了减少所涉及的内存。那并不像您认为的那样。是的,它创建了一个压缩的 JPEG 表示,但您不对其进行任何操作。你只是在丢弃它。如果您想优化内存并且图像是高分辨率的,请考虑将图像调整为适合单元格的大小。有关调整类别大小的示例,请参阅 UIImage resizing not working properly。
考虑使用 SDWebImage or AFNetworking 的
UIImageView
类别,它可以延迟加载和缓存图像。问题中的这段代码在启动异步检索之前不会正确重置图像视图。如果在图像检索完成之前重复使用单元格,它将不会处理这种情况。它可能会不必要地重新检索几秒钟前下载的图像。等等
有很多与优雅地异步检索图像相关的问题,这两个 类 的
UIImageView
类别为您处理了很多这些细节。