0Pricing
Swift Academy · 课时

Sendable 协议

标记可安全跨越并发域的类型。

Sendable 协议 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。

Sendable 的含义

Sendable是一种标记协议。遵循它的类型承诺可以安全地跨并发域共享,而不会引入数据竞争。

它没有任何方法;它是由编译器检查的线程安全保证。

protocol Sendable {}

值类型通常是 Sendable

如果结构体和枚举的所有存储属性都是Sendable,它们就可以自动遵循该协议。

由于传递值时会进行复制,每个任务都会获得自己独立的副本。

struct User: Sendable {
    let id: Int
    let name: String
}

隐式遵循

许多值类型会隐式获得Sendable:具有 Sendable 成员的非公开冻结结构体和枚举、由 Sendable 类型组成的元组,以及元类型。

对于内部值类型,您通常完全不需要写出: Sendable。

enum Direction { case north, south, east, west }
// Implicitly Sendable: no associated non-Sendable data

何时结构体是 NOT 可发送的

如果结构体持有一个非 Sendable 的存储属性,它就不是 Sendable。

这里的类引用使共享的可变状态变得可访问,因此编译器拒绝自动实现一致性。

final class Box { var value = 0 }
struct Wrapper { // not Sendable
    let box: Box // class with mutable state
}

类与 Sendable

类是引用类型,因此副本共享同一个实例。类只有在标记为不可继承,且其所有存储属性都是不可变并且为 Sendable 时,才是 Sendable。

final class ImmutablePoint: Sendable {
    let x: Double
    let y: Double
    init(x: Double, y: Double) { self.x = x; self.y = y }
}

@unchecked Sendable

有时您知道某个类是线程安全的(它使用锁保护状态),但编译器无法证明这一点。请使用 @unchecked Sendable 手动声明其安全性。

这是一条安全逃生通道:您需要自行对正确性负责。

import Foundation
final class Cache: @unchecked Sendable {
    private let lock = NSLock()
    private var storage: [String: Int] = [:]
    func set(_ k: String, _ v: Int) {
        lock.lock(); defer { lock.unlock() }
        storage[k] = v
    }
}

@Sendable 闭包

闭包也可以标记为 @Sendable。这样的闭包可以跨越并发边界传递,因此编译器会验证它只捕获 Sendable 值。

func run(_ work: @Sendable () -> Void) {
    work()
}
let name = "Ada" // immutable, Sendable
run { print(name) }

捕获可变状态会被拒绝

@Sendable 闭包不能捕获可变变量,因为两个任务可能会同时修改它。

编译器会标记下面的捕获。

var counter = 0
// Error: @Sendable closure captures mutable var
// run { counter += 1 }

异步接口中的 Sendable

并发入口点要求使用 Sendable。Task 闭包是 @Sendable 的,传递给其他参与者的值也必须是 Sendable。

类型系统正是通过这种方式阻止非线程安全数据在任务之间泄漏。

func process(_ user: User) async {
    Task {
        // user is Sendable, safe to capture
        print(user.name)
    }
}

泛型 Sendable 约束

您可以在泛型中要求 Sendable。一个使用参数启动任务的函数,应将该参数约束为 Sendable。

func dispatch<T: Sendable>(_ value: T) {
    Task { print(value) }
}

Sendable 与参与者

Sendable 回答的是“这个值是否可以安全传递?”,而 actor 回答的是“如何保护可变状态?”

两者可以组合使用:参与者会隐式成为 Sendable,因为对其状态的访问是串行化的。

actor Logger { } // actors are implicitly Sendable

快速检查:Sendable

请测试您对 Sendable 一致性的理解。

回顾:Sendable 协议

Sendable 用于标记可以安全跨越并发边界的类型。不可变值类型会自动实现一致性;不可继承的不可变类可以实现一致性;线程安全的类则使用 @unchecked Sendable。

@Sendable 闭包只能捕获 Sendable 值,异步接口也要求使用 Sendable,以防止不安全的共享。Sendable 与参与者一起构成了 Swift 数据竞争安全的类型级基础。

常见问题解答

「Sendable 协议」课时是免费的吗?

是的 — 「Sendable 协议」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。

「Sendable 协议」这节课中我会学到什么?

标记可安全跨越并发域的类型。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「Sendable 协议」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Swift Academy 课中编写并运行代码吗?

能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 数据竞争问题
  2. Sendable 协议
  3. Actor 隔离与 nonisolated
  4. 迁移到严格并发
← 返回 Swift Academy