Swift 并发:我到底该不该用 Actor?——一张决策图帮你拍板

Actor 是什么?(一句话版)

Actor = 自带大门的房间:一次只能进一个人,进门要"等钥匙"(await)。

它存在的唯一理由:保护非 Sendable 的可变状态。

Actor vs Class:只差一个隔离域

维度 Class Actor
引用语义
继承
隔离域 ❌(谁都能同步访问) ✅(必须 await进门)
线程安全 手动锁/队列 编译器保证
同步调用 任意 外部禁止

把 Actor 想成"远程服务": 数据在"服务器"里,你要发请求(await)才能读写。

决策三要素:缺一不可!

只有同时满足下面 3 条,才值得上 Actor:

  1. 有非 Sendable 状态

    (纯 Sendable 结构体/类 → 无需保护)

  2. 操作必须原子性

    (读-改-写必须打包,不能中途被插)

  3. 这些原子操作

    不能在现有 Actor 上完成(如 MainActor)

缺一条 → 用别的方式 原因
只有 ① 缺 ② Sendable+ 值类型即可
有 ①② 但能在 MainActor 做 直接标 @MainActor,还能同步访问 UI
为了"避开主线程"而造 Actor 反模式!用 @concurrent/ Task.detached即可

反例集合:这些 Actor 都"师出无名"

❌ 网络客户端 Actor

swift 复制代码
actor APIClient {
    // 全是 Sendable:URLSession、tokenString
    func request() async -> Data { ... }
}
  • 状态已 Sendable → 无需保护
  • 副作用只是"不想跑主线程"→ 用 @concurrent 函数即可
  • 结果:人为加锁,解码都无法并发

❌ "看不懂并发报错"就套 Actor

swift 复制代码
@globalActor actor MyRandomActor {
    // 空状态,只为消 Sendable 警告
}

→ 永远别用 Actor 当创可贴!

先理解警告,再选工具(Sendable@MainActor@concurrent)。

正例:真正需要 Actor 的场景

✅ 本地非 Sendable 缓存

swift 复制代码
actor ImageCache {
    private var store: [URL: UIImage] = [:]   // UIImage 非 Sendable
    func image(for url: URL) async -> UIImage? {
        if let img = store[url] { return img }
        let data = try await URLSession.shared.data(from: url).0
        let img = UIImage(data: data)!
        store[url] = img
        return img
    }
}
  • 状态非 Sendable
  • 读-写-缓存必须原子
  • MainActor 不适合(网络+解码耗时)

✅ 协议强制 Sendable

swift 复制代码
protocol DataSource: Sendable {
    func fetch() async -> [Item]
}
  • 实现层含非 Sendable 状态 → 只能用 Actor 满足 Sendable

决策流程图

arduino 复制代码
需要共享可变状态?
├─ 否 → 用 struct / class(Sendable)
├─ 是 → 状态 Sendable?
│   ├─ 是 → 用 Sendable 值类型或锁自由类
│   └─ 否 → 操作必须原子?
│       ├─ 否 → 拆成 Sendable 片段
│       └─ 是 → 能在 MainActor 完成?
│           ├─ 是 → @MainActor
│           └─ 否 → **上 Actor** ✅

口诀:

"Sendable 先,MainActor 其次,新 Actor 最后。"

同步访问红线:Actor = "远程服务"

外部调用必须异步:

swift 复制代码
actor Counter {
    func increment() { value += 1 }
}

// 外部
await counter.increment()   // ✅
counter.increment()         // ❌ 编译失败

→ 如果你无法容忍这种异步接口(例如实时音频回调),

根本不该用 Actor ------ 考虑锁、原子类或 @concurrent 函数。

常见误解速答

误解 真相
"Actor 让并发更快" 它更安全而非更快;异步排队可能更慢
"把类改成 actor 就能消并发警告" 治标不治本;先理解 Sendable 要求
"网络层必须 actor" 若状态 Sendable,用 @concurrent函数/任务即可
"actor 里所有代码都异步" 内部可完全同步;只有外部调用需 await

一句话总结

"Actor 是保护'非 Sendable 可变状态'的昂贵保险箱------ 确认你真的有宝贝,且别处放不下,再把它请回家。"

记住三要素:

  1. 非 Sendable 状态
  2. 必须原子操作
  3. 现有 Actor 帮不上

同时满足 → 用 Actor;缺一条 → 找更简单的工具。

让 Actor 留在真正需要串行大门的地方,别把远程服务的复杂度,带进本可并行的小花园。

相关推荐
大熊猫侯佩13 小时前
Swift 6 驱魔实录:揭开 Combine 与 @Sendable 的“血色契约”
swift·block·combine·preconcurrency·sendable·mainactor·isolation
初级代码游戏14 小时前
iOS开发 SwiftUI 15:手势 拖动 缩放 旋转
ios·swiftui·swift
ujainu17 小时前
Flutter + OpenHarmony 游戏开发进阶:虚拟摄像机系统——平滑跟随与坐标偏移
开发语言·flutter·游戏·swift·openharmony
初级代码游戏3 天前
iOS开发 SwiftUI 14:ScrollView 滚动视图
ios·swiftui·swift
初级代码游戏3 天前
iOS开发 SwitftUI 13:提示、弹窗、上下文菜单
ios·swiftui·swift·弹窗·消息框
zhyongrui4 天前
托盘删除手势与引导体验修复:滚动冲突、画布消失动画、气泡边框
ios·性能优化·swiftui·swift
zhangfeng11334 天前
CSDN星图 支持大模型微调 trl axolotl Unsloth 趋动云 LLaMA-Factory Unsloth ms-swift 模型训练
服务器·人工智能·swift
zhyongrui5 天前
SnipTrip 发热优化实战:从 60Hz 到 30Hz 的性能之旅
ios·swiftui·swift
大熊猫侯佩5 天前
Neo-Cupertino 档案:撕开 Actor 的伪装,回归 Non-Sendable 的暴力美学
swift·observable·actor·concurrency·sendable·nonsendable·data race
2501_915921437 天前
在没有源码的前提下,怎么对 Swift 做混淆,IPA 混淆
android·开发语言·ios·小程序·uni-app·iphone·swift