5-9.【性能分析与优化】struct + mutating 与 class + 引用共享 在并发场景下,性能和安全性如何取舍?

1 阅读2分钟

1️⃣ 值类型(struct + mutating

安全性

  • 天然线程安全:每个线程拿到的是 struct 的 独立副本,互不干扰。
  • 快照语义:读写不会影响其他线程的数据副本。
  • CoW 注意:如果 struct 内部含 CoW 容器(数组、字典、字符串),写入时可能触发复制,这复制可能在堆上,但仍然线程安全(因为 CoW 会先复制)。

性能

  • 复制开销

    • 小 struct:开销极小,栈拷贝快。
    • 大 struct(包含数组、buffer):每次修改可能触发 CoW,或者大量数据拷贝,CPU 和内存压力大。
  • 批量更新可优化:局部变量修改后再整体赋值,减少 CoW。

并发场景建议

  • 每个线程维护自己的 struct 副本 → 安全且无需锁

  • 对共享大数组/CoW 容器,需要注意:

    • 写操作触发 CoW → 堆分配 → 可能影响性能
    • 多线程频繁写 → 每次可能触发 CoW → 性能下降

2️⃣ 引用类型(class + 共享对象)

安全性

  • 默认线程不安全

    • 多线程访问同一个 class 对象时,读写可能冲突
    • 需要加锁(DispatchQueue, NSLock, actor)或者使用 @MainActor/actor 进行同步
  • 共享状态:所有线程看到同一个实例的修改

性能

  • 拷贝成本低

    • 无需复制 struct 的大字段
    • 对大型 buffer/数组频繁修改非常高效
  • 锁/同步开销

    • 多线程需要保护数据 → CPU 上下文切换或锁等待 → 性能可能下降
    • 对于高频修改,如果锁粒度太大 → 可能成为瓶颈

并发场景建议

  • 大对象需要频繁修改 → class 性能优越
  • 多线程访问 → 使用细粒度锁或 actor/队列保证安全
  • 内存共享 → 减少堆分配和 CoW

3️⃣ 对比总结表格

维度struct + mutatingclass + 共享引用
线程安全天然安全(独立副本)默认不安全,需要锁或 actor
写入性能小 struct快,大 struct触发 CoW慢高,直接修改堆内存,无额外复制
内存开销多副本 → 栈 + 堆单实例 → 堆 + 引用
并发易用性简单,无锁复杂,需要锁或同步策略
最适用场景并发读多写少,快照需求,UI state高频修改,大数据共享,多线程频繁写入

4️⃣ 核心权衡思路

  1. 安全优先 → struct

    • 适合 SwiftUI、Redux 风格架构
    • 快照、undo/redo、跨线程传递安全
  2. 性能优先 → class

    • 适合大 buffer、频繁修改、多线程写入
    • 必须加同步手段保证线程安全
  3. 混合策略

    • 小字段用 struct 保持值语义
    • 大字段或共享缓冲用 class 包装
    • 结构清晰,读写性能可控,安全性可通过 actor 或锁保证

💡 实例示意:

// 高安全性版本(struct + mutating)
struct UIState {
    var counter: Int
    var data: [Int]  // CoW
}
func update(state: inout UIState) {
    state.counter += 1
}

// 高性能版本(class + 引用共享)
class UIStateClass {
    var counter: Int = 0
    var data: [Int] = []
    let queue = DispatchQueue(label: "ui.state.queue")
    
    func update() {
        queue.sync { counter += 1 }  // 同步写
    }
}