Swift 中 Sendable 的使用
在 Swift 中,Sendable 是一种协议,用于标记类型是否可以安全地跨线程传递。这是 Swift 并发模型(从 Swift 5.5 引入)的一部分,主要用于确保并发环境中数据传递的安全性。
Sendable 的概念
Sendable表示一个类型的实例可以安全地从一个线程传递到另一个线程。- 它是一个 marker protocol(标记协议),即协议本身没有要求实现任何方法或属性。
- 许多标准库类型(如
Int、String、Array)已经默认符合Sendable。
在使用并发功能(如 Task、Actor)时,编译器会检查类型是否遵循 Sendable,以确保数据传递的线程安全性。
为什么需要 Sendable
在多线程环境中,某些类型可能会因为并发访问而导致数据竞争或未定义行为。Sendable 提供了一个静态安全机制,帮助开发者提前捕获潜在问题。
例如:
- 值类型(如
struct)可以跨线程传递,因为它们是不可变的或每个线程有独立的副本。 - 引用类型(如
class)可能在多线程访问时引发问题,需要特殊处理。
Sendable 的自动符合
值类型
- 如果一个值类型的所有成员类型都符合
Sendable,该值类型自动符合Sendable。
struct MyStruct: Sendable {
var value: Int // Int 是 Sendable
}
引用类型
- 类默认不符合
Sendable,因为它们可能会在并发环境中引发数据竞争。 - 需要显式声明为
Sendable并确保其线程安全性。
final class MyClass: Sendable {
let value: Int // 只读属性是线程安全的
}
自定义类型实现 Sendable
如果类型的线程安全性不能由编译器自动推断,开发者可以通过显式声明遵守 Sendable,并使用 @unchecked 关键字告诉编译器我们确认其是线程安全的。
示例:显式声明 Sendable
final class CustomClass: @unchecked Sendable {
let value: Int
init(value: Int) {
self.value = value
}
}
- 使用
@unchecked表示开发者自己保证线程安全,编译器不再检查。
Sendable 的使用场景
在并发任务中传递数据
当在并发任务中使用共享数据时,Sendable 可确保数据传递是安全的。
struct User: Sendable {
let name: String
let age: Int
}
func fetchUserData() async -> User {
return User(name: "Alice", age: 25)
}
Task {
let user = await fetchUserData()
print("User: \(user)")
}
与 Actor 配合
Actor 的隔离模型依赖于 Sendable,只有符合 Sendable 的类型才能在 Actor 间安全传递。
actor UserManager {
func updateUser(name: String) -> String {
return "Updated \(name)"
}
}
let manager = UserManager()
Task {
let result = await manager.updateUser(name: "Alice")
print(result)
}
标准库中符合 Sendable 的类型
以下类型默认符合 Sendable:
- 值类型:
Int、String、Array、Dictionary、Set等。 - 一些引用类型:
URL、UUID等。
编译器检查
如果在并发代码中使用不符合 Sendable 的类型,编译器会报错。例如:
class NonSendableClass {
var value = 0
}
func performTask(data: NonSendableClass) async {
// 编译器报错:NonSendableClass does not conform to Sendable
}
let data = NonSendableClass()
Task {
await performTask(data: data)
}
注意事项
- 引用类型线程安全性:对引用类型使用
@unchecked Sendable时需确保其内部状态是线程安全的。 - 可变属性:在符合
Sendable的类型中,使用let限制属性为不可变。 - 静态分析:Swift 编译器在异步和并发环境中会自动检查类型是否符合
Sendable。
总结
Sendable是 Swift 并发模型的重要组成部分,确保类型可以安全地在线程间传递。- 值类型通常自动符合
Sendable,引用类型需要开发者手动声明或确保线程安全。 - 使用
Sendable的检查可以帮助捕获并发中的潜在问题,是编写健壮代码的一个重要工具。