Sendable — 这是什么,线程安全性协议

作者: IT Sectr 发布日期: 2026-06-18 阅读时间: 10 分钟

Sendable — 是一个 Swift 协议,用于标记可在线程之间安全传输的类型。在使用并发编程(actor、async/await、Task)时,Swift 编译器要求在隔离上下文之间传输的所有数据都必须符合 Sendable。这排除了意外传输可能导致数据竞争的不安全类型。该协议作为一份合约:符合 Sendable 的类型保证不存在内部未同步状态。据 WWDC 2021,Sendable 协议是设计安全多线程架构时的必要元素。

主要内容

  • Sendable — 保证在线程之间传输时不会发生数据竞争的协议
  • 值类型(struct、enum)如果所有属性都是 Sendable,则自动符合 Sendable
  • @unchecked Sendable — 针对开发者自行承担安全性的类的绕过机制
  • Sendable 闭包 使用 @Sendable 属性标记,该属性检查变量捕获
  • 编译器 在非 Sendable 类型在隔离上下文之间传输时报错

Swift 中的 Sendable 协议是什么?

Sendable — 是来自 Swift 标准库(SE-0302)的一个协议,用于标记可在隔离域之间安全传输的类型。隔离域包括 actor、Task 任务或 @MainActor 上下文。Sendable 不包含任何要求 — 它作为安全标记。

为什么需要 Sendable

在 Swift 5.5 之前,开发者可以通过 DispatchQueue 在线程之间传输任何对象,而编译器不会检查这是否安全。Sendable 封闭了这个漏洞:现在编译器自己跟踪线程间传输并阻止不安全的传输。这使得并发代码在运行之前就更加可靠。

swift
struct UserProfile: Sendable {
    let name: String
    let age: Int
}

class NonSendableClass {
    var counter: Int = 0
}

在示例中,UserProfile 可以安全地在线程之间传输,因为它是一个具有 Sendable 类型常量属性的结构体。NonSendableClass 在尝试通过 await 或 Task 传输时将引起编译错误。

哪些类型默认符合 Sendable

Sendable 类型 在 Swift 中分为三类:具有 Sendable 属性的值类型、具有不可变状态的 final 类以及用 @Sendable 标记的函数/闭包。编译器为值类型自动推导 Sendable 合规性。

值类型和自动合规性

Struct、enum 和 tuple 如果所有属性和关联值也都是 Sendable,则自动成为 Sendable。这是一种保守方法:如果即使只有一个属性不符合 Sendable,整个类型将不会被视为安全。

类型Sendable条件
Struct是(无需显式指示)所有属性 Sendable
Enum是(无需显式指示)所有关联值 Sendable
Final class是(需要显式指示)所有 let 属性 Sendable,无 var
Non-final class因继承原因无法成为 Sendable

基础类型

Swift 的所有内置类型 — Int、String、Double、Bool、Optional、Array、Dictionary、Set — 都符合 Sendable。这使得类型组合默认是安全的。开发者只需关注自定义类。

Sendable 与类:@unchecked Sendable

@unchecked Sendable — 是一种机制,允许类显式声明自己为 Sendable,绕过编译器的检查。开发者承担此类的线程安全性责任。这对于 Objective-C 桥接和优化结构很有用。

何时使用 @unchecked Sendable

@unchecked Sendable 在类内部通过锁或原子操作保证安全性时应用,但编译器无法静态检查此事。例如,一个含有 os_unfair_lock 或 pthread_mutex_t 的类 — 其线程安全性由代码保证,但 Swift 无法检测到。

swift
final class AtomicCounter: @unchecked Sendable {
    private var value: Int = 0
    private let lock = NSLock()
    
    func increment() {
        lock.lock()
        value += 1
        lock.unlock()
    }
}

谨慎使用 @unchecked Sendable。这是给其他开发者的一个清晰信号:“我已经检查过,这是安全的”。实现中的错误可能导致难以追踪的竞争条件。在应用 @unchecked 之前,请确保该类型确实无法重写为值类型。

Sendable 与 Actor 的组合

Actor 和 Sendable 是同一枚硬币的两面。Actor 隔离其状态,但与外界交换数据时必须返回 Sendable 类型。如果 actor 方法返回非 Sendable 类型,编译器将发出警告或错误。

通过 Sendable 传输数据

当 actor 向外部代码发送数据时,这些数据越过了隔离边界。Sendable 保证接收方可以在 actor 外部安全地使用它们。Actor 本身保持隔离 — 其内部状态不会暴露。

swift
struct AccountSnapshot: Sendable {
    let id: UUID
    let balance: Double
    let lastUpdated: Date
}

actor BankActor {
    private var balance: Double = 0
    
    func snapshot() async -> AccountSnapshot {
        return AccountSnapshot(
            id: UUID(),
            balance: balance,
            lastUpdated: Date()
        )
    }
}

AccountSnapshot — 是一个 Sendable 结构体,只包含 Sendable 类型的 let 属性。这种方法是从 actor 提取数据的最佳实践。状态快照按值传输,actor 不会失去对其状态的控制。

Sendable 函数和闭包

@Sendable — 是用于函数和闭包的属性,确保闭包不以可变方式捕获非 Sendable 数据。当闭包被传递到 Task 或 actor 方法时,它必须是 Sendable。

@Sendable 闭包中的捕获检查

编译器检查 @Sendable 闭包是否捕获了可变引用。允许捕获 Sendable 类型的 let 属性。捕获引用类型的 var 变量将引起错误,因为闭包可能与变更同时执行。

swift
func performAsync(operation: @Sendable () -> Void) {
    Task {
        await operation()
    }
}

let constant = "Safe"
var counter = 0
// counter — 捕获 var 会变更计数器,编译错误
performAsync { // ❌ 已捕获 var 的变更
    print(constant)
}

规则很简单:@Sendable 闭包只能捕获那些本身是Sendable且不会被外部变更的数据。对于类,如果类未标记为 Sendable,则允许捕获 weak self 弱引用。这防止了经典的引用循环和竞争条件。

常见问题

用简单的话来说,Swift 中的 Sendable 是什么?

Sendable — 是一个标记,告诉编译器:“这个类型可以在线程之间安全地传输”。结构体和常量通常默认是 Sendable,类则不是,除非显式指定。

哪些类型自动符合 Sendable?

值类型(struct、enum)具有 Sendable 属性、具有不可变状态的 final 类以及所有 Swift 基础类型:Int、String、Double、Bool、Array、Dictionary、Optional — 自动符合 Sendable。

什么是 @unchecked Sendable?

@unchecked Sendable — 是一种无需编译器静态检查就声明类为 Sendable 的方法。开发者自行保证安全性,通常通过锁或原子操作。

为什么 Actor 需要 Sendable?

Actor 隔离其状态,但当数据返回给外部代码时,这些数据越过了隔离边界。Sendable 保证接收方在 actor 外部使用这些数据时不会遇到竞争条件。

如何在 Swift 中将类变为 Sendable?

Sendable 协议添加到一个 final 类,其所有属性都是 Sendable 类型的常量。如果类使用锁,可以应用 @unchecked Sendable,但这需要谨慎。

总结

  • Sendable — 用于线程间安全数据传输的标记协议
  • 值类型 在符合条件时自动符合 Sendable
  • 需要显式指示 Sendable 或 @unchecked Sendable
  • Actor 只通过 Sendable 类型将数据返回给外部代码
  • @Sendable 闭包属性检查变量捕获的正确性
  • 编译器 静态检查 Sendable 合规性,在构建阶段防止竞争条件

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读