使用 dispatch_async 在 UICollectionView 中加载图片

Posted

技术标签:

【中文标题】使用 dispatch_async 在 UICollectionView 中加载图片【英文标题】:Using dispatch_async to load images in UICollectionView 【发布时间】:2015-07-13 23:48:13 【问题描述】:

我正在使用 Parse 来存储图像。我正在尝试异步加载图像,这样它就不会干扰collectionView 滚动。我是使用dispatch_async 的新手,不太确定如何正确实施它。我也研究过延迟加载,但我认为这会起作用。但事实并非如此,collectionView 滚动条很不稳定。谢谢

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
    albumImageCell *cell = (albumImageCell *) [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];

    if (cell == nil) 
        cell = [[albumImageCell alloc]init];
    

    PFObject *temp = [_dataArray objectAtIndex:indexPath.row];
    PFFile *file = [temp objectForKey:@"image"];

    if (cell.hasImage == FALSE) 
        dispatch_async(imageQueue, ^
            NSData *data = [file getData];
                if (data) 
                    dispatch_async(dispatch_get_main_queue(), ^(void)
                        cell.imageView.image = [UIImage imageWithData:data];
                        cell.hasImage = TRUE;
                    );

                
        );
    

    return cell;
 

【问题讨论】:

这段代码存在的问题超出了波动性。在后台加载图像时,可以重复使用单元格。您可能会得到连接到特定单元格的错误图像。我认为我见过的最好的解决方案是创建一个异步 UIImageView 类,该类在后台自行加载。我不知道这个特定的是否好,但您可以尝试一下:github.com/nicklockwood/AsyncImageView @EricS 是的,这曾经发生过。你认为我可以在我的自定义collectionViewCell 文件而不是子类UIImageView 中加载和设置图像吗? 当然,但是您必须通过实现prepareForReuse 方法来处理单元格被重用的情况。你会想要取消现有的网络请求,或者至少忽略结果,然后创建一个新请求。请记住,第二个请求可能会在第一个请求之前返回 - 无法保证它们会以任何特定顺序返回。 【参考方案1】:

因为UIImage +imageWithData 方法有点阻塞主线程。

dispatch_async(dispatch_get_main_queue(), ^(void)
    cell.imageView.image = [UIImage imageWithData:data];

所以这些行应该如下所示。

UIImage *image = [UIImage imageWithData:data];

dispatch_async(dispatch_get_main_queue(), ^(void)
    cell.imageView.image = image;

此外,在 UIImage 对象实际显示或渲染之前,UIImage 不会立即解码图像。见https://***.com/a/19251240/629118。因此,以下代码是摆脱不连贯滚动的最佳方法之一。

UIImage *image = [UIImage imageWithData:data];

UIGraphicsBeginImageContext(CGSizeMake(1,1));
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, CGRectMake(0, 0, 1, 1), [image CGImage]);
UIGraphicsEndImageContext();

dispatch_async(dispatch_get_main_queue(), ^(void)
    cell.imageView.image = image;

【讨论】:

我们对这个问题的解决方案是在后台线程而不是 UIImage 上创建一个 CGImageRef 并将 CGImageRef 传递给主线程。主线程将从 CGImageRef 创建一个 UIImage,这非常快。 (UIImage 在早期版本的 ios 中也不是线程安全的,所以我们养成了从不在辅助线程上使用它们的习惯)

以上是关于使用 dispatch_async 在 UICollectionView 中加载图片的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用 dispatch_async() 方法时 AsyncSocket 无法连接到主机?

为啥在 requestAccessToEntity:completion 中使用 performSegueWithIdentifier 时使用 dispatch_async?

将 dispatch_async 与 self 一起使用

dispatch_async 与 NSOperation 队列

iOS - 使用 CoreData 的 dispatch_async 保留周期

(iOS) dispatch_async() 与 NSOperationQueue