PHImageManager的requestImage在浏览数千张图片时会锁定UI。

3

我有一个集合视图,用于显示所有用户的照片。基本的东西,数据源通过获取 PHAssets 并在 PHCachingImageManager 上使用 requestImage 来加载缩略图。

但是最近我收到了一个错误报告,当您拥有超过 5000 张图像并快速浏览时,UI 会冻结。经过调查,我能够重现此问题,并且似乎主线程在调用 requestImage 后被锁定 (_lock_wait),而由该调用创建的数十个其他线程(用于其他单元格的缩略图)也被锁定等待谁知道什么。

我尝试了几个方法都不起作用:

  1. 实现了 UICollectionViewDataSourcePrefetching,并在 prefetchItemsAt 事件上使用 PHCachingImageManager 开始缓存图像。有趣的是,它使事情变得更糟,因为它尝试一次加载更多的图像。而且当单元格离开屏幕时应该调用的 cancelPrefetchingForItemsAt 从未被调用。

  2. 尝试在较慢的请求上调用 cancelImageRequest,也没有成功。

现在我不知道还能做什么,我可以在后台 dispatch_queue 上运行 requestImage,这样它就不会锁定我的主线程,但是感觉很奇怪,因为该方法本身将生成另一个线程。

我的代码大致如下:

let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = .fast
requestOptions.deliveryMode = .opportunistic
requestOptions.isSynchronous = false
requestOptions.isNetworkAccessAllowed = true
return requestOptions

myManager.requestImage(for: asset, targetSize: CGSize(width: 375, height: 375), contentMode: .aspectFill, options: options) { /* use image */ }

PS: 我不确定,但似乎这只发生在iOS 11上,目前我只能在iPhone X上复制


你是否将同步模式设置为true进行了检查? - karthikeyan
我正在进行测试,它的效果更好,但是我失去了在高质量图像加载完成之前加载更快、质量较低的图像的选项... - Paulo Cesar
哦,我仍然得到了锁定,但现在是在应用程序的后期阶段,当我设置当前的OpenGL上下文时。这真的很奇怪。 - Paulo Cesar
似乎在重用单元格时使用cancelImageRequest已经解决了问题!但我仍需要与客户确认。如果这有效,我会更新答案。 - Paulo Cesar
1个回答

2
原来问题是: GCD调度并发队列在崩溃日志中出现“Dispatch Thread Soft Limit Reached: 64”导致冻结 requestImage 不断创建新线程,直到达到调度线程限制,导致UI在_lock_wait上冻结。
解决方法是将requestOptions.isSynchronous = true,创建一个具有有限并发性的OperationQueue,并将图像获取作为该队列上的作业添加。
//somewhere like viewDidLoad
operationQueue.maxConcurrentOperationCount = 54

//when setting up the cells
operationQueue.addOperation { [weak self] in
      self?.cachingImageManager.requestImage(/* params...*/) { (image) in
            OperationQueue.main.addOperation {
               //use image
            }
      }
}

1
在这种情况下,如何在快速滚动时获取低质量图像?另外,如何管理请求取消? - demon9733

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接