原文:Global actor in Swift Concurrency explained with code examples -- SwiftLee
什么是 Global Actor?
概念 | 一句话解释 |
---|---|
Actor | 一种引用类型,串行化地执行对其状态的访问,天然线程安全。 |
Global Actor | 全局唯一的 Actor,可被标注到 任意函数 / 属性 / 类型,让所有访问都在同一条串行队列上执行。 |
一句话:把「线程同步」这件事从手动加锁升级为「编译器帮你保证」。
系统自带:@MainActor
最常见、也是 iOS 开发者天天打交道的全局 Actor。
swift
// 1. 标注函数 → 保证主线程
@MainActor
func updateUI() {
label.text = "Done" // 永远在主线程执行
}
// 2. 标注属性 → 所有读写都在主线程
@MainActor
var titles: [String] = []
// 3. 标注整个类 → 类内所有成员默认主线程
@MainActor
final class ContentViewModel {
var titles: [String] = []
}
凡是 UI 更新、KVO、NotificationCenter 的 post/observe,都建议加上
@MainActor
,省去手动DispatchQueue.main.async
。
自定义 Global Actor:三步走
当你需要把 一类业务逻辑 放到同一条串行执行队列时(如图像处理、磁盘缓存、音频渲染...),自建全局 Actor 是利器。
Step 1:声明 Actor + @globalActor
swift
@globalActor
actor ImageProcessing {
static let shared = ImageProcessing() // 必须实现
private init() {} // ✅ 建议私有,防止乱 new
}
@globalActor
告诉编译器:这是一个「全局单例」Actor。- 必须提供
static let shared
,否则会编译错误。 - 把
init()
设为private
,避免外部ImageProcessing()
创建新实例。
Step 2:贴标签即可用
swift
// 1. 标注类 → 类内所有成员自动走 ImageProcessing 队列
@ImageProcessing
final class ImageCache {
private var store: [URL: Data] = [:]
func store(_ data: Data, for url: URL) {
store[url] = data // 自动线程安全
}
}
// 2. 标注单个函数
@ImageProcessing
func applyFilter(_ image: UIImage) -> UIImage {
// 耗时滤镜计算
return image
}
Step 3:跨 Actor 调用
swift
@MainActor
class EditorViewModel {
private let cache = ImageCache() // 也在 ImageProcessing 上
func saveThumbnail(for url: URL) async {
let image = UIImage(data: try! Data(contentsOf: url))!
let filtered = await applyFilter(image) // ✅ 跨 Actor,用 await
await cache.store(filtered.pngData()!, for: url)
}
}
- 任何跨 Actor 访问都要
await
,编译器会强制你遵守。 - 不再需要手动
DispatchQueue
/NSLock
,彻底告别数据竞争。
常见疑问 & 最佳实践
问题 | 解答 |
---|---|
可以用在 struct / enum 吗? | ✅ 可以,只要挂上 @YourGlobalActor 。 |
能继承吗? | ❌ Actor 是引用类型,不能继承;但你可以把 Actor 包装在类里。 |