gpt4 book ai didi

objective-c - 使用后台线程处理单元重用的正确方法?

转载 作者:太空狗 更新时间:2023-10-30 03:25:10 28 4
gpt4 key购买 nike

我有一个 UICollectionView,但同样的方法应该适用于 UITableViews。我的每个单元格都包含我从磁盘加载的图像,这是一个缓慢的操作。为了缓解这种情况,我使用了一个异步调度队列。这工作正常,但快速滚动会导致这些操作堆叠起来,这样单元格将按顺序将其图像从一个图像更改为另一个图像,直到最终在最后一次调用时停止。

过去,我检查过单元格是否仍然可见,如果不可见,我就不会继续。但是,这不适用于 UICollectionView,而且效率不高。我正在考虑迁移到使用 NSOperations,它可以被取消,这样只有最后一次修改单元格的调用才会通过。我可以通过检查操作是否已在 prepareForReuse 方法中完成来完成此操作,如果没有则取消它。我希望过去有人处理过这个问题,并可以提供一些提示或解决方案。

最佳答案

Session 211 - Building Concurrent User Interfaces on iOS, from WWDC 2012 ,讨论了随着后台任务 catch (从 38 分钟 15 秒开始),单元格图像发生变化的问题。

这是解决该问题的方法。将图像加载到后台队列后,使用索引路径查找当前单元格(如果有)显示包含该图像的项目。如果你得到一个单元格,设置单元格的图像。如果得到 nil,则当前没有显示该项目的单元格,因此只需丢弃该图像即可。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = nil;
dispatch_async(myQueue, ^{
[self bg_loadImageForRowAtIndexPath:indexPath];
});
return cell;
}

- (void)bg_loadImageForRowAtIndexPath:(NSIndexPath *)indexPath {
// I assume you have a method that returns the path of the image file. That
// method must be safe to run on the background queue.
NSString *path = [self bg_pathOfImageForItemAtIndexPath:indexPath];
UIImage *image = [UIImage imageWithContentsOfFile:path];
// Make sure the image actually loads its data now.
[image CGImage];

dispatch_async(dispatch_get_main_queue(), ^{
[self setImage:image forItemAtIndexPath:indexPath];
});
}

- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
// If cell is nil, the following line has no effect.
cell.imageView.image = image;
}

这是一种减少后台任务加载 View 不再需要的图像的“堆积”的简单方法。给自己一个 NSMutableSet 实例变量:

@implementation MyViewController {
dispatch_queue_t myQueue;
NSMutableSet *indexPathsNeedingImages;
}

当您需要加载图像时,将单元格的索引路径放入集合中,并调度一个任务,从集合中取出一个索引路径并加载其图像:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];
cell.imageView.image = nil;
[self addIndexPathToImageLoadingQueue:indexPath];
return cell;
}

- (void)addIndexPathToImageLoadingQueue:(NSIndexPath *)indexPath {
if (!indexPathsNeedingImages) {
indexPathsNeedingImages = [NSMutableSet set];
}
[indexPathsNeedingImages addObject:indexPath];
dispatch_async(myQueue, ^{ [self bg_loadOneImage]; });
}

如果单元格停止显示,则从集合中删除单元格的索引路径:

- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
[indexPathsNeedingImages removeObject:indexPath];
}

要加载图像,请从集合中获取索引路径。我们必须为此分派(dispatch)回主队列,以避免集合上的竞争条件:

- (void)bg_loadOneImage {
__block NSIndexPath *indexPath;
dispatch_sync(dispatch_get_main_queue(), ^{
indexPath = [indexPathsNeedingImages anyObject];
if (indexPath) {
[indexPathsNeedingImages removeObject:indexPath];
}
}

if (indexPath) {
[self bg_loadImageForRowAtIndexPath:indexPath];
}
}

bg_loadImageForRowAtIndexPath:方法同上。但是当我们真正在单元格中设置图像时,我们还想从集合中移除单元格的索引路径:

- (void)setImage:(UIImage *)image forItemAtIndexPath:(NSIndexPath *)indexPath {
MyCell *cell = (MyCell *)[collectionView cellForItemAtIndexPath:indexPath];
// If cell is nil, the following line has no effect.
cell.imageView.image = image;

[indexPathsNeedingImages removeObject:indexPath];
}

关于objective-c - 使用后台线程处理单元重用的正确方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13921946/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com