You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 6严格并发下ImagePickerView的数据竞争问题求助

Swift 6严格并发模式下ImagePickerView数据竞争问题解决方法

核心问题根源

你的问题本质是跨actor访问共享数据导致的并发竞争:

  • UIImagePickerController的delegate回调可能不在主actor线程执行,直接修改主actor隔离的parent或图片列表会触发数据竞争警告。
  • 即使包裹Task { @MainActor in },如果parent本身未被正确标记为actor隔离,依然会出现竞争。

分步解决代码示例

以下是完整的修正方案,针对常见的SwiftUI + UIImagePickerController场景:

1. 给ViewModel标记主Actor隔离

如果你的parent是ObservableObject类型,必须显式标记@MainActor,确保所有属性访问和修改都在主线程:

@MainActor
class PhotoViewModel: ObservableObject {
    @Published var photos: [UIImage] = []
}

2. 给ImagePickerView标记主Actor隔离

SwiftUI视图默认在主actor执行,但严格并发下显式标记更可靠:

@MainActor
struct ImagePickerView: UIViewControllerRepresentable {
    @ObservedObject var viewModel: PhotoViewModel
    @Binding var isPresented: Bool

    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = .camera // 或 .photoLibrary
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}

    func makeCoordinator() -> Coordinator {
        Coordinator(viewModel: viewModel, isPresented: $isPresented)
    }

    // Coordinator直接持有ViewModel和Binding,避免多层捕获self
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        private let viewModel: PhotoViewModel
        private let isPresented: Binding<Bool>

        init(viewModel: PhotoViewModel, isPresented: Binding<Bool>) {
            self.viewModel = viewModel
            self.isPresented = isPresented
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard let image = info[.originalImage] as? UIImage else {
                isPresented.wrappedValue = false
                return
            }

            // 显式切换到主actor上下文执行数据修改
            Task { @MainActor in
                viewModel.photos.append(image)
                isPresented.wrappedValue = false
            }
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            isPresented.wrappedValue = false
        }
    }
}

关键修正点说明

  • Actor隔离标记:通过@MainActor确保PhotoViewModel和ImagePickerView的所有操作都在主线程,消除跨actor访问的竞争风险。
  • Coordinator优化:直接传递ViewModel和Binding,避免捕获整个ImagePickerView实例,减少并发下的捕获风险。
  • 显式主actor任务:在delegate回调中用Task { @MainActor in }包裹数据修改操作,强制在主actor上下文执行,即使回调本身不在主线程也能保证安全。

额外注意事项

  • 如果你的parent是普通结构体而非ObservableObject,改用@Binding传递图片列表的修改权限,确保修改操作在主actor执行。
  • 避免在非主actor闭包中直接捕获self,优先传递必要的属性或绑定,减少并发依赖。

内容的提问来源于stack exchange,提问作者Jaxon Irvine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:53:21