滚动浏览数千张图像时,PHImageManager 的 requestImage 锁定 UI

PHImageManager's requestImage locks UI when scrolling through thousands of images

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

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

我尝试了几种方法,但没有任何效果:

  1. 已实施 UICollectionViewDataSourcePrefetching,并使用 PHCachingImageManagerprefetchItemsAt 事件上启动缓存图像。有趣的是,它让事情变得更糟,因为它试图一次加载更多图像。 cancelPrefetchingForItemsAt 本应在单元格超出屏幕时被调用,但从未被调用。

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

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

我的代码是这样的:

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[=21 上重现=]

事实证明这是问题所在:GCD dispatch concurrent queue freeze with 'Dispatch Thread Soft Limit Reached: 64' in crash log

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
            }
      }
}