为何向异步函数传递non-sendable类型未触发Sendable合规警告?
我实现了两个逻辑完全一致的类型:BankAccount(actor类型)和AlternativeBankAccount(普通类,未遵循Sendable协议)。但将AlternativeBankAccount实例传递给异步函数charge时,编译器并未触发任何警告。明明异步上下文从random方法切换到了charge函数,我原本以为这会强制要求charge的所有参数必须符合Sendable协议,希望有人能解释原因。
运行日志
1 - <_NSMainThread: 0x600001704040>{number = 1, name = main} 2 - <NSThread: 0x600001711100>{number = 7, name = (null)}
代码实现
actor BankAccount { enum BankError: Error { case insufficientFunds } var balance: Double init(initialDeposit: Double) { self.balance = initialDeposit } func withdraw(amount: Double) throws { guard balance >= amount else { throw BankError.insufficientFunds } balance -= amount } func deposit(amount: Double) { balance += amount } } class AlternativeBankAccount { enum BankError: Error { case insufficientFunds } var balance: Double init(initialDeposit: Double) { self.balance = initialDeposit } func withdraw(amount: Double) throws { guard balance >= amount else { throw BankError.insufficientFunds } balance -= amount } func deposit(amount: Double) { balance += amount } } struct Charger { func charge(amount: Double, from bankAccount: isolated BankAccount, to otherAccount: AlternativeBankAccount) async throws -> (Double, Double) { print("2 - \(Thread.current)") try bankAccount.withdraw(amount: amount) let newBalance = bankAccount.balance otherAccount.deposit(amount: amount) return (newBalance, otherAccount.balance) } } @MainActor class ViewModel { let charger = Charger() func random(bankAccount: BankAccount) { let bankAccount2 = AlternativeBankAccount(initialDeposit: 200) Task { print("1 - \(Thread.current)") let aa = try? await charger.charge(amount: 100, from: bankAccount, to: bankAccount2) print("balance", aa?.0 ?? "", aa?.1 ?? "") } } }
原因解析
默认并发检查级别宽松:Swift默认的
Strict Concurrency Checking设置为Minimal,这个级别下编译器不会严格检查非Sendable类型跨异步上下文/线程的传递。只有将该选项设置为Targeted或Complete时,才会触发相关警告。异步函数不强制参数为Sendable:异步函数本身不会自动要求所有参数都遵循Sendable协议,只有当参数需要在不同actor/任务之间传递(即跨线程访问)时,Sendable才是必须的——它用来保证类型的线程安全。你的代码中
AlternativeBankAccount是引用类型,没有任何线程安全保障,跨线程传递会有数据竞争风险,但默认编译设置下编译器不会主动检测这个问题。如何触发警告:在Xcode的项目设置中,找到
Build Settings->Swift Compiler - Language->Strict Concurrency Checking,将其改为Complete。此时编译器会明确提示AlternativeBankAccount未遵循Sendable协议,且无法安全跨线程传递。
内容的提问来源于stack exchange,提问作者Laura Corssac

