Swift 并发:Actor、isolated、nonisolated 完全导读

Actor 是什么?

Actor 是引用类型(与 class 类似),但自带隔离域:

  • 任意时刻只允许一个任务进入 Actor 内部
  • 外部调用必须加 await,自动排队
  • 编译器保证无数据竞争,无需手动加锁

对比:手动锁 vs Actor

swift 复制代码
// ❌ 旧世界:DispatchQueue + 锁
class Counter {
    private var value = 0
    private let queue = DispatchQueue(label: "counter")
    
    func increment() {
        queue.sync { value += 1 }
    }
}

// ✅ 新世界:Actor
actor Counter {
    var value = 0
    
    func increment() {
        value += 1          // 无需锁,编译器保护
    }
}

→ 代码量减半,永远不会忘记加锁。

Actor 使用范式

swift 复制代码
let counter = Counter()

Task {
    await counter.increment()           // 必须 await
    let current = await counter.value   // 串行排队
    print("Value \(current)")
}

规则速记:"进 Actor 先 await,出来无需 await。"

nonisolated: actor 里的"快速通道"

当方法不触碰 Actor 的任何可变状态时,可标 nonisolated

  • 跳过排队 → 性能更高
  • 无需 await → 调用方更轻松
  • 但不能读/写任何属性(除非属性也是 nonisolated

示例

swift 复制代码
actor Logger {
    nonisolated func appName() -> String {
        "MyCoolApp"   // 只返回常量,无状态访问
    }
    
    func log(_ msg: String) {
        print("[\(Date())] \(msg)")  // 访问状态,需 await
    }
}

Task {
    print(Logger().appName())      // ✅ 同步调用
    await Logger().log("Hello")    // ✅ 需 await
}

isolated:把"外部函数"拉进 actor 内部

场景:在 extension 或工具方法里,想同步访问 actor 状态,又不想 await

做法:把 actor 自身作为参数标 isolated

swift 复制代码
actor StatsTracker {
    var total = 0
    var count = 0
    
    func showAverage() {
        // 身处 actor 内部,可直接调用
        print(average(from: self))
    }
}

extension StatsTracker {
    // 外部函数,但获得"内部通行证"
    func average(from tracker: isolated StatsTracker) -> Double {
        Double(tracker.total) / Double(tracker.count)   // ✅ 无 await
    }
}

func average1(from tracker: isolated StatsTracker) -> Double {
    Double(tracker.total) / Double(tracker.count)   // ✅ 无 await
}

Task {
    let st = StatsTracker()
    // 这里调用的时候需要加await
    let s = await average1(from: st)
}

调用限制:

  • 只能在同一 actor 隔离域里使用
  • 外部世界无法直接 await average() → 编译器挡住

实战:UserService 完整例子

swift 复制代码
actor UserService {
    private var cache: [Int: String] = [:]

    // Actor 隔离:可能挂起
    func fetchUser(id: Int) async -> String {
        if let name = cache[id] { return name }
        
        try? await Task.sleep(nanoseconds: 500_000_000) // 模拟网络
        let name = "User\(id)"
        cache[id] = name
        return name
    }
    
    // 非隔离:立即返回
    nonisolated func version() -> String {
        "UserService v1.0"
    }
}

使用侧:

swift 复制代码
let service = UserService()

Task {
    print(service.version())              // 同步,无 await
    print(await service.fetchUser(id: 42)) // 第一次,慢
    print(await service.fetchUser(id: 42)) // 第二次,缓存瞬间返回
}

三条口诀一张图

场景 关键词 是否 await 性能
进 actor 内部 await 正常排队
不碰状态的工具 nonisolated 快速通道
外部函数想进内部 isolated Actor ❌(内部用) 零开销

常见编译错误速查

错误 原因 修复
Actor-isolated property can only be referenced with 'await' 少写 await 加上 await
Call to nonisolated function must not be marked with 'await' 多了 await 去掉 await
'isolated' parameter can only be called from within the same actor 外部直接调 isolated方法 把逻辑移到 actor 内部或使用普通扩展

什么时候用 Actor: checklist

✅ 适合

  • 多任务同时读/写同一可变状态(计数器、缓存、状态机)
  • 需要编译期保证无数据竞争
  • 与 SwiftUI、CoreData 主线程栈搭配

❌ 不适合

  • 只读常量或纯函数 → 用 struct
  • 跨隔离频繁大量小调用 → 队列可能成为瓶颈
  • 已有无锁队列/原子方案且极致性能

一句话总结

"Actor = 自动锁 + 编译器检查;nonisolated 开快道,isolated 让外部函数进内部。"

把 Actor 当作"线程安全保险箱":

重要物品(可变状态)放进去,拿的时候await排队;

只读文件(nonisolated)走侧门,不排队;

临时工具(isolated)让内部员工用,零等待。

用好这三板斧,写并发代码就像写单线程一样轻松!

相关推荐
代码s贝多芬的音符1 小时前
ios android 小程序 蓝牙 CRC16_MODBUS
android·ios·小程序
非专业程序员Ping2 小时前
从0到1自定义文字排版引擎:原理篇
ios·swift·assembly·font
2501_915909062 小时前
“绑定 HTTPS” 的工程全流程 从证书配置到真机验证与故障排查
网络协议·http·ios·小程序·https·uni-app·iphone
2501_915918413 小时前
iOS 混淆实战 多工具组合完成 IPA 混淆、加固与工程化落地(iOS混淆|IPA加固|无源码混淆|Ipa Guard|Swift Shield)
android·ios·小程序·https·uni-app·iphone·webview
游戏开发爱好者86 小时前
如何系统化掌握 iOS 26 App 耗电管理,多工具协作
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_915921437 小时前
运营日志驱动,在 iOS 26 上掌握 App 日志管理实践
android·macos·ios·小程序·uni-app·cocoa·iphone
马拉萨的春天9 小时前
iOS的多线程下数据安全和内存泄漏以及工具使用监测内存泄漏
macos·ios·cocoa·多线程
2501_9151063216 小时前
HTTPS 爬虫实战指南 从握手原理到反爬应对与流量抓包分析
爬虫·网络协议·ios·小程序·https·uni-app·iphone
2501_9160074716 小时前
iOS 上架技术支持全流程解析,从签名配置到使用 开心上架 的实战经验分享
android·macos·ios·小程序·uni-app·cocoa·iphone
HarderCoder17 小时前
【Swift 筑基记】把“结构体”与“类”掰开揉碎——从值类型与引用类型说起
swift