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

为何向异步函数传递non-sendable类型未触发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 ?? "")
        }

    }
}

原因解析

  1. 默认并发检查级别宽松:Swift默认的Strict Concurrency Checking设置为Minimal,这个级别下编译器不会严格检查非Sendable类型跨异步上下文/线程的传递。只有将该选项设置为Targeted或Complete时,才会触发相关警告。

  2. 异步函数不强制参数为Sendable:异步函数本身不会自动要求所有参数都遵循Sendable协议,只有当参数需要在不同actor/任务之间传递(即跨线程访问)时,Sendable才是必须的——它用来保证类型的线程安全。你的代码中AlternativeBankAccount是引用类型,没有任何线程安全保障,跨线程传递会有数据竞争风险,但默认编译设置下编译器不会主动检测这个问题。

  3. 如何触发警告:在Xcode的项目设置中,找到Build Settings -> Swift Compiler - Language -> Strict Concurrency Checking,将其改为Complete。此时编译器会明确提示AlternativeBankAccount未遵循Sendable协议,且无法安全跨线程传递。

内容的提问来源于stack exchange,提问作者Laura Corssac

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 10:36:05