Swift 6编译错误:Task/Actor隔离值无法发送的解决咨询
解决Xcode 16 Beta 3 + Swift 6并发完整模式下
Task or actor isolated value cannot be sent编译错误 问题概述
使用Xcode 16.0 beta 3、Swift 6并启用Complete Concurrency模式时,编译触发错误:Task or actor isolated value cannot be sent。错误发生在CNContactPickerDelegate的回调方法中,尝试将非Sendable类型的[CNContact]数组赋值给MainActor隔离的@Binding属性时出现。此前Xcode 16早期beta版本未出现该问题,尝试过自定义CNContact复制结构体、MainActor.assumeIsolated等WWDC2024提及的策略均无效。
错误代码
import SwiftUI @preconcurrency import Contacts import ContactsUI struct SimpleContactPicker: View { let allowedContactType: CNContactType? = CNContactType.person @Binding var selectedContacts: [CNContact]? var body: some View { SimpleContactPickerProxy(allowedContactType: self.allowedContactType, selectedContacts: $selectedContacts) .ignoresSafeArea() } } extension CNContactType { var predicateForEnablingContact: NSPredicate { NSPredicate(format: "contactType=\(self.rawValue)") } } struct SimpleContactPickerProxy: UIViewControllerRepresentable, @unchecked Sendable { typealias UIViewControllerType = CNContactPickerViewController let allowedContactType: CNContactType? @Binding var selectedContacts: [CNContact]? final class Coordinator: NSObject, CNContactPickerDelegate, @unchecked Sendable { var parent: SimpleContactPickerProxy init(_ parent: SimpleContactPickerProxy) { self.parent = parent } func contactPickerDidCancel(_ picker: CNContactPickerViewController) { } func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) { Task { @MainActor in self.parent.selectedContacts = contacts // <<-- ERROR: Task or actor isolated value cannot be sent } } } func makeUIViewController(context: Context) -> CNContactPickerViewController { let contactPickerViewController = CNContactPickerViewController() contactPickerViewController.delegate = context.coordinator if let allowedContactType { contactPickerViewController.predicateForEnablingContact = allowedContactType.predicateForEnablingContact } return contactPickerViewController } func updateUIViewController(_ uiViewController: CNContactPickerViewController, context: Context) { } func makeCoordinator() -> Coordinator { Coordinator(self) } }
解决方案
方案1:使用unsafeTransfer绕过Sendable检查(推荐)
CNContact是不可变类型,本质上线程安全,只是苹果未将其标记为Sendable。使用Swift 6新增的unsafeTransfer函数,明确告知编译器我们保证该值的线程安全性,从而绕过并发检查:
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) { Task { @MainActor in self.parent.selectedContacts = unsafeTransfer(contacts) } }
方案2:将更新逻辑封装到MainActor标记的方法中
在Coordinator中定义被@MainActor标记的方法处理属性更新,同时用unsafeTransfer传递非Sendable的数组:
final class Coordinator: NSObject, CNContactPickerDelegate, @unchecked Sendable { var parent: SimpleContactPickerProxy init(_ parent: SimpleContactPickerProxy) { self.parent = parent } func contactPickerDidCancel(_ picker: CNContactPickerViewController) { } func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) { updateSelectedContacts(unsafeTransfer(contacts)) } @MainActor private func updateSelectedContacts(_ contacts: [CNContact]) { parent.selectedContacts = contacts } }
方案3:确认回调线程后直接赋值(简化版)
UIKit的CNContactPickerDelegate代理方法默认在主线程执行,可直接去掉Task { @MainActor in }包裹赋值:
func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) { self.parent.selectedContacts = contacts }
注意:若后续Contacts框架变更回调线程,该方案可能引发线程安全问题,建议优先使用前两种方案。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

