滚动浏览数千张图像时,PHImageManager 的 requestImage 锁定 UI
PHImageManager's requestImage locks UI when scrolling through thousands of images
我有一个集合视图,其中显示所有用户的照片。基本的东西,一个数据源,它获取 PHAssets 并在 PHCachingImageManager
上使用 requestImage
来加载缩略图。
但最近我收到了一个错误报告,其中 UI 当您拥有超过 5000 张图像并快速滚动浏览时会冻结。经过调查,我能够重现该问题,似乎主线程在调用 requestImage
后立即被锁定 (_lock_wait),而该调用创建的许多其他线程(缩略图其他单元格)也被锁定等待谁知道。
我尝试了几种方法,但没有任何效果:
已实施 UICollectionViewDataSourcePrefetching
,并使用 PHCachingImageManager
在 prefetchItemsAt
事件上启动缓存图像。有趣的是,它让事情变得更糟,因为它试图一次加载更多图像。 cancelPrefetchingForItemsAt
本应在单元格超出屏幕时被调用,但从未被调用。
尝试在较慢的请求上调用 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
}
}
}
我有一个集合视图,其中显示所有用户的照片。基本的东西,一个数据源,它获取 PHAssets 并在 PHCachingImageManager
上使用 requestImage
来加载缩略图。
但最近我收到了一个错误报告,其中 UI 当您拥有超过 5000 张图像并快速滚动浏览时会冻结。经过调查,我能够重现该问题,似乎主线程在调用 requestImage
后立即被锁定 (_lock_wait),而该调用创建的许多其他线程(缩略图其他单元格)也被锁定等待谁知道。
我尝试了几种方法,但没有任何效果:
已实施
UICollectionViewDataSourcePrefetching
,并使用PHCachingImageManager
在prefetchItemsAt
事件上启动缓存图像。有趣的是,它让事情变得更糟,因为它试图一次加载更多图像。cancelPrefetchingForItemsAt
本应在单元格超出屏幕时被调用,但从未被调用。尝试在较慢的请求上调用
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
}
}
}