UIImage initWithData:从异步调度中阻塞 UI 线程?

Posted

技术标签:

【中文标题】UIImage initWithData:从异步调度中阻塞 UI 线程?【英文标题】:UIImage initWithData: blocking UI thread from async dispatch? 【发布时间】:2015-09-11 22:19:15 【问题描述】:

以下代码阻止了 UI(在图像加载完成之前无法点击另一个选项卡)。

我已经通过注释该行并保持下载(我知道这是异步发生并在主线程上调用完成处理程序)来验证是 UIImage *imageToShow = [[UIImage alloc] initWithData:data]; 调用是罪魁祸首。

在仅注释initWithData: 行的情况下,UI 响应很好。但是,如果在后台明确调度,那条线怎么可能是支撑 UI 的线呢?

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
    ...

    [objectStore getFileInContainer:@"public_images" filename:filename completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) 
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
            UIImage *imageToShow = [[UIImage alloc] initWithData:data];
            dispatch_async(dispatch_get_main_queue(), ^
                collectionImageView.image = imageToShow;
            );
        );
    ];

    ...

【问题讨论】:

即使注释掉collectionImageView.image = imageToShow; 行,UI 是否仍会锁定? 另外,你下载的图片有多大? 我应该更清楚 - 当我注释掉 initwithdata 行时,我也注释掉了 collectionimageview.image 行。图片非常大 - 每张图片平均大约 1 MB。 【参考方案1】:

UIImage 在第一次实际使用/绘制图像之前不会读取和解码图像。要强制这项工作在后台线程上进行,您必须在执行主线程-setImage: 调用之前在后台线程上使用/绘制图像。许多人认为这违反直觉。我在another answer 中非常详细地解释了这一点。

【讨论】:

【参考方案2】:

最有可能导致 UI 挂起的部分是图像到图像视图的实际设置,尤其是在图像很大的情况下,因为这确实(并且必须)发生在主线程上。

我建议在您仍在后台线程中操作时先调整图像大小或缩小图像。然后,一旦您调整了图像大小,就跳回主线程并将其分配给“imageView.image”。

这是调整 UIImage 大小的一种方法的简单示例:Resize UIImage by keeping Aspect ratio and width

【讨论】:

以上是关于UIImage initWithData:从异步调度中阻塞 UI 线程?的主要内容,如果未能解决你的问题,请参考以下文章

未能从 Swift 继承 NSInputStream(initWithData:无法识别的选择器)

如何从异步方法向调用者抛出异常?

加载图像异步并返回UIImage的公共函数

为啥所有异步图像加载(SDWebImage、AFNetworking)库都使用 UIImageView 而不是 UIImage?

自定义 UITableViewCell - UIImageView 的异步 UIImage

防止 UITableViewCell 的 UIImageView 调整异步加载的 UIImage 的大小