5-21.【性能分析与优化】Time Profiler 中看到大量 swift_retain / swift_release,你会如何分析?

4 阅读2分钟

1️⃣ 现象

Time Profiler 中,你看到:

swift_retain
swift_release
swift_retain
swift_release
...
  • 频繁调用 → CPU 占用高 → 热路径性能下降

  • 通常出现在:

    • class 实例频繁创建 / 传递
    • 闭包捕获 / 临时对象
    • 协议存在类型 / 泛型值类型逃逸到堆
    • 数组 / 字典写操作触发 CoW 并逃逸到 heap

2️⃣ 分析思路

(1)确认对象类型

  • ARC 只对 class / heap 对象 / heap 逃逸 struct 生效

  • 检查:

    • 是否频繁创建临时 class
    • 是否值类型(struct / enum)由于 逃逸 被放到 heap → 引用计数操作

(2)检查调用上下文

  • 使用 Extended Detail → Call Tree,展开函数栈

  • 找到 swift_retain / swift_release 的调用者

  • 常见热点:

    1. 协议存在类型调用(existential) → 值类型被 box → heap + ARC
    2. 泛型函数返回逃逸 struct → heap + ARC
    3. SwiftUI / Combine pipeline 中 closure 捕获 class

(3)结合 Allocations 工具

  • 对应 Time Profiler 栈展开 → Allocations 查看对象数量
  • 如果对象频繁分配 → retain/release 频繁
  • 可以判断 是短生命周期对象频繁堆分配,还是 长生命周期对象频繁引用修改

3️⃣ 常见优化手段

(1)减少 heap 分配 / 值类型逃逸

struct BigStruct {
    var data: [Int]
}

func process(_ x: BigStruct) { ... }
  • 如果 x 逃逸 → heap + ARC

  • 优化:

    • 使用 inout 传递,避免拷贝到 heap
    • 使用局部副本或批量操作
func processInout(_ x: inout BigStruct) { ... }

(2)减少协议存在类型 boxing

  • [Protocol] / let p: Protocol = ... → 值类型被 box → ARC

  • 优化:

    • 泛型约束代替 protocol existential → 静态派发
  • 示例:

protocol Shape { func area() -> Double }
func totalArea<T: Shape>(_ shapes: [T]) -> Double {
    shapes.reduce(0) { $0 + $1.area() } // 静态派发 + 无 ARC boxing
}

(3)closure 捕获优化

let array = [1,2,3]
let doubled = array.map { $0 * 2 } // closure 捕获
  • 小闭包捕获堆分配 class → retain/release

  • 优化:

    • 避免捕获大量对象
    • 使用局部 struct 或泛型函数

(4)热点函数内联

  • 小函数 → @inline(__always) → 编译器可优化 retain/release
  • 避免临时对象频繁堆分配

(5)分析 SwiftUI / Combine 情况

  • SwiftUI view struct 频繁更新 → heap escape + ARC

  • Combine pipeline sink / map / assign closure 捕获 class → retain/release

  • 优化策略:

    • 尽量用 struct / 泛型代替 class
    • 避免 protocol existential / AnyPublisher / AnyObject 捕获

4️⃣ 总结分析流程

  1. 确认类型:class / heap struct / closure → ARC

  2. 确认调用点:Time Profiler 展开 call tree

  3. 配合 Allocations:判断对象频繁分配

  4. 诊断原因

    • 临时 class / heap 逃逸 struct
    • 协议存在类型 boxing
    • closure 捕获 class
  5. 优化方法

    • 泛型代替协议存在类型
    • inout / 局部副本
    • final class / struct 优化静态派发
    • 内联 / 小函数减少 retain/release

核心理念:
频繁 retain/release = 对 heap 对象操作频繁 → 静态派发 + 值类型 + 泛型 + inout + 内联 → 可显著降低 ARC 开销