标记@MainActor的update方法刷新collectionView仍崩溃,原因何在?
问题原因与解决方法
核心问题
PHPhotoLibraryChangeObserver的photoLibraryDidChange回调是在后台串行队列触发的,并非主线程。你虽然给update标记了@MainActor,但直接在后台线程同步调用这个方法时,Swift的Actor机制并不会自动切换到主线程执行代码——因为@MainActor的调度逻辑需要结合**异步上下文(async/await)**或者显式的MainActor上下文切换才能生效,同步调用无法触发线程切换,导致collectionView.reloadData()仍在后台线程执行,最终触发UI操作的崩溃。
解决方法
以下几种方式都能解决问题,选一种适合你的即可:
方法1:用Task显式切换到MainActor上下文调用update
extension PhotosCollectionViewController: PHPhotoLibraryChangeObserver { func photoLibraryDidChange(_ changeInstance: PHChange) { Task { @MainActor in update() } } @MainActor private func update() { collectionView.reloadData() } }
方法2:将update改为async方法,通过await调用
extension PhotosCollectionViewController: PHPhotoLibraryChangeObserver { func photoLibraryDidChange(_ changeInstance: PHChange) { Task { await update() } } @MainActor private func update() async { collectionView.reloadData() } }
方法3:直接在update内用MainActor.run(兼容旧写法)
如果不想改动调用逻辑,也可以在update方法内显式指定UI操作在主线程执行:
@MainActor private func update() { MainActor.run { collectionView.reloadData() } }
补充说明
GCD的DispatchQueue.main.async之所以能正常工作,是因为它直接将UI操作任务派发到主线程队列;而@MainActor的作用是标记代码属于MainActor的执行上下文,只有在异步调用或者显式切换到MainActor上下文时,才能确保代码在主线程运行,同步调用无法触发这个调度逻辑。
内容的提问来源于stack exchange,提问作者Karina
相关产品推荐
相关产品推荐

