actor
等新特性。其中, actor
是专门为解决 数据竞争(data race)问题而设计的结构化并发组件。在传统的多线程编程中,开发者常常需要依赖锁(如 DispatchQueue
, NSLock
等)来手动同步数据访问,这既容易出错,又难以维护。 actor
的出现,大大简化了并发编程的复杂性。
本文将全面讲解 Swift 中的 actor
,包括其定义、使用方式、与 class
的对比、使用场景等,帮助大家更深入地理解并掌握它。
actor
是 Swift 中一种引用类型(reference type),它的主要特性是:它内部的状态是线程隔离的,只有一个任务能在任意时间访问其可变状态。换句话说,Swift 会自动对 actor
实例的访问进行序列化处理,防止数据竞争。
actor Counter {
private var value = 0
func increment() {
value += 1
}
func getValue() -> Int {
return value
}
}
在上面的示例中,Counter
是一个 actor,它的 value
属性只能通过该 actor 的方法访问,并且 Swift 会自动保证对这些方法的调用是线程安全的。
定义一个 actor 与定义一个 class 非常相似,只需将关键字 class
替换为 actor
即可:
actor MyActor {
var state = 0
func doSomething() {
state += 1
}
}
Swift 的 actor 提供了一个更现代、结构化且安全的方式来管理共享状态,避免常见的并发编程陷阱:
不需要手动加锁,actor 会将对其内部可变状态的访问自动进行串行化处理。
Actor 将可变状态封装在内部,外部只能通过异步方法访问,增强封装性。
Actor 天然支持 async/await,在异步函数中以直观的方式使用,代码更简洁清晰。
虽然 actor 和 class 都是引用类型,它们在并发模型下的行为存在显著差异。
特性 | class | actor |
---|---|---|
并发安全 | ❌ 需要开发者手动控制 | ✅ 自动并发安全 |
可变状态访问 | 直接访问 | 需通过 async 方法访问(默认) |
继承支持 | ✅ 支持继承 | ❌ 不支持继承,只能遵循协议 |
并发执行行为 | 不限制,可能产生 data race | 内部状态串行访问,避免竞争 |
这使得在需要并发访问共享状态时,actor 更加合适。
由于 actor 会对外部调用进行隔离,大多数 actor 的方法(非 nonisolated
)都需要使用 await
调用。
let counter = Counter()
Task {
await counter.increment()
let currentValue = await counter.getValue()
print("Current value: \(currentValue)")
}
actor Counter {
private var value = 0
func increment() {
value += 1 // OK:actor 内部访问自身状态不需要 await
}
func valuePlusTen() -> Int {
return value + 10
}
}
nonisolated
用法有时候我们希望某个方法可以在不跳出并发隔离的上下文下被访问,这时可以用 nonisolated
:
actor Logger {
nonisolated func logInfo(_ msg: String) {
print("[INFO] \(msg)") // 不访问 actor 内部状态,所以可标注为 nonisolated
}
}
MainActor
这是一个特殊的 actor,表示代码必须在主线程执行,常用于 UI 更新或主线程约束逻辑。
@MainActor
class ViewModel {
var title: String = ""
func updateTitle(_ newTitle: String) {
title = newTitle
}
}
在 SwiftUI 或 UIKit 中操作 UI 时,推荐使用 @MainActor
来保证线程安全。
actor 可以遵循协议,但在定义协议时要注意异步接口的约定:
protocol ScoreUpdatable {
func increaseScore() async
}
actor GameManager: ScoreUpdatable {
private var score = 0
func increaseScore() async {
score += 1
}
}
如果协议中包含异步方法,实现时也必须使用 async
。
虽然 actor 为我们带来了结构化并发和自动的线程安全,但它并非万能:
场景 | 是否适合使用 actor |
---|---|
有状态共享 | ✅ 非常合适 |
多线程并发访问 | ✅ 自动隔离访问 |
无需继承机制 | ✅ actor 不支持继承 |
对性能极端敏感 | ❌ 可能影响性能 |
UI 更新 | ✅ 使用 @MainActor |
使用 actor
是 Swift 并发编程的重要组成部分,能帮助我们以更安全、简洁的方式编写线程安全代码。虽然不是所有场景都需要 actor,但在状态共享、异步逻辑以及协程结构化控制中,它都是非常有价值的工具。
@MainActor
,避免主线程拥塞。掌握 actor
的正确用法,是写好现代 Swift 并发代码的关键一步。如果你还未在项目中尝试 actor,现在是时候开始了。
最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。