我有一个图像选择器结构,我用它来拍摄照片并将其存储为列表,但是由于 Swift 6 严格并发,我一生都无法让它工作。
这是原始结构
struct ImagePickerView: UIViewControllerRepresentable {
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate, PHPickerViewControllerDelegate {
var parent: ImagePickerView
init(_ parent: ImagePickerView) {
self.parent = parent
}
// Handle image picker for camera capture
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
if let imageData = image.jpegData(compressionQuality: 0.6) {
parent.imageList.append(ImageData(
_id: "\(UUID())",
imageData: imageData
))
}
}
picker.dismiss(animated: true)
}
// Handle picker for selecting from library
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider else { return }
if provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { image, _ in
DispatchQueue.main.async {
if let imageData = (image as? UIImage)?.jpegData(compressionQuality: 0.6) {
self.parent.imageList.append(ImageData(
_id: "\(UUID())",
imageData: imageData
))
}
}
}
}
}
}
@Binding var imageList: [ImageData]
@Binding var isCamera: Bool
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
func makeUIViewController(context: Context) -> UIViewController {
if isCamera {
// Camera access using UIImagePickerController
let imagePicker = UIImagePickerController()
imagePicker.sourceType = .camera
imagePicker.delegate = context.coordinator
return imagePicker
} else {
// Photo Library using PHPickerViewController
var config = PHPickerConfiguration()
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
在选择器函数中,if let imageData = (image as? UIImage)?,图像被标记为,
Sending 'image' risks causing data races.
Task-isolated 'image' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses.
我正在努力了解如何修复此错误,我尝试将调度队列作为任务运行 { @MainActor in } 但这导致 self.parent 说发送 self 会导致数据争用。
您正在尝试使用 GCD 解决 Swift 并发隔离问题,这通常不是一个好主意。
loadObject 完成处理程序需要
Sendable
一致性,因为它可以跨并发域传递。这也意味着所有捕获的值必须符合 Sendable
。
请参阅SE-0302了解更多详情。
您的
Coordinator
已经绑定到 MainActor
,因为您已经在 ImagePickerView
中声明了它,它通过 UIViewControllerRepresentable
接收 actor 绑定。这意味着 Coordinator
已经符合 Sendable
。
所以你不需要做这方面的任何事情。您所要做的就是调整完成处理程序中的调用:
// Handle picker for selecting from library
func picker(
_ picker: PHPickerViewController,
didFinishPicking results: [PHPickerResult]
) {
picker.dismiss(animated: true)
guard let provider = results.first?.itemProvider, provider.canLoadObject(ofClass: UIImage.self) else {
return
}
provider.loadObject(ofClass: UIImage.self) { image, _ in
guard let imageData = (image as? UIImage)?.jpegData(compressionQuality: 0.6) else {
return
}
Task { @MainActor in
self.parent.imageList.append(ImageData(
_id: UUID().uuidString,
imageData: imageData
))
}
}
}
请注意,这会捕获完成处理程序中的“self”以及其中的任务。 如果您想防止这种情况或有取消机制,则必须相应地添加它。但是,这不是您实际问题的一部分。