Sendable 协议-Swift 结构化并发的核心安全保障

Sendable 是 Swift 5.5+ 为并发安全 设计的标记协议,核心作用是:告诉编译器「这个类型的实例可以安全地跨 Task/线程传递」,不会引发数据竞争

一、核心本质

  1. Sendable 是「空协议」(无需要实现的方法),仅作为编译期检查标记,不影响运行时性能;
  2. 遵循 Sendable 的类型,要么是「不可变的」(值类型+只读属性),要么是「线程安全的」(如加锁、Actor 封装)。

二、哪些类型默认遵循 Sendable?

无需手动声明,Swift 内置类型已默认遵循:

  • 基础值类型:Int/String/Bool/Double/Array(不可变)/Dictionary(不可变);
  • 原子类型:Atomic(Swift 5.9+);
  • 空元组:()
  • 不可变枚举:无关联值/关联值也遵循 Sendable 的枚举。

三、实用示例

1. 自定义类型遵循 Sendable

场景1:不可变值类型(直接遵循)
swift 复制代码
// 所有属性都是 let(不可变),直接遵循 Sendable
struct User: Sendable {
    let id: Int
    let name: String
    let createTime: Date
}

// 跨 Task 传递:编译器判定安全
Task {
    let user = User(id: 1, name: "张三", createTime: Date())
    Task {
        print(user.name) // 无数据竞争风险
    }
}
场景2:可变类型(需保证线程安全)
swift 复制代码
import OSAtomic

// 可变类:通过原子操作保证线程安全后遵循 Sendable
final class Counter: Sendable {
    private var _count: Int32 = 0 // 原子整型
    
    // 原子操作读写,保证线程安全
    var count: Int {
        get { Int(OSAtomicAdd32(0, &_count)) }
        set { OSAtomicStore32(Int32(newValue), &_count) }
    }
}

2. 编译期安全检查(核心价值)

如果类型不遵循 Sendable 却跨 Task 传递,编译器会直接报错,提前拦截数据竞争风险:

swift 复制代码
// 未遵循 Sendable 的可变类型
struct UnsafeData {
    var value: String // 可变,无线程安全保障
}

// 编译报错:传递非 Sendable 类型存在数据竞争风险
Task {
    var data = UnsafeData(value: "test")
    Task {
        data.value = "error" // ❌ 编译器直接提示错误
    }
}

四、关键使用场景

  1. Task/Async Closure 传参:跨并发任务传递的数据必须遵循 Sendable;
  2. Actor 通信:Actor 之间传递的消息(参数/返回值)必须遵循 Sendable;
  3. 全局变量/缓存:并发场景下的全局数据,标记为 Sendable 保证安全;
  4. 闭包捕获:异步闭包中捕获的变量,编译器会检查是否遵循 Sendable。

五、常见坑点

  1. 可变容器var array: [Int] 是可变数组,不遵循 Sendable;let array: [Int](不可变)才遵循;
  2. 类类型:类默认不遵循 Sendable(即使属性是 let),需手动声明并保证线程安全;
  3. 嵌套类型:嵌套类型需也遵循 Sendable,外层类型才能遵循。

总结

  1. Sendable 是编译期并发安全标记,无实际方法需实现;
  2. 不可变值类型可直接遵循,可变类型需保证线程安全后遵循;
  3. 核心作用:由编译器提前拦截跨线程数据竞争风险,而非运行时处理;
  4. 是 Swift 结构化并发的核心安全保障,异步编程中必须关注。
相关推荐
鹤卿1233 分钟前
Block基础
开发语言·ios·objective-c
开开心心loky27 分钟前
[OC 底层] (二)类与对象底层原理
macos·ios·objective-c·cocoa
ZZH_AI项目交付15 小时前
扫脸功能交给 SDK 后,主工程里的旧代码怎么删除
ios·app·apple
ZZH_AI项目交付16 小时前
扫脸功能做成 SDK,为什么我没有把结果页和历史记录一起搬进去
ios·app
茶底世界之下16 小时前
诡异!String 参数在闭包里变成了 <uninitialized>,我排查了整整两天
ios·xcode·swift
四眼蒙面侠18 小时前
深入 Open Agent SDK(四):多 Agent 协作——子代理、团队与任务编排
swift·agentsdk·openagentsdk
harder32119 小时前
iOS IPA 马甲包送审风险评估工具
ios
SameX20 小时前
存钱 App 开发手记:restitution 0.3 是怎么试出来的,以及 86400 秒不等于一天
ios
MonkeyKing1 天前
蓝蓝牙核心基础概念详解:2.4GHz频段、跳频、信道、广播、连接、配对
android·ios