2-13.【OC】【内存管理】ARC 是否意味着“没有性能成本”?

1 阅读3分钟

简单直接的答案是:不,ARC 不仅有性能成本,甚至在某些特定情况下比手动管理(MRC)还要高。

虽然 ARC 的初衷是自动化和安全,但“自动化”的代价是由 CPU 的时钟周期和内存开销来支付的。我们可以从以下四个维度来看 ARC 的“隐藏账单”:


1. 冗余的运行时函数调用

在 MRC 中,你只在必要时写 [obj retain]。但在 ARC 中,为了保证绝对安全,编译器往往采取保守策略

  • 原子性与保护:当你读取一个属性时,ARC 可能会插入 objc_loadWeakRetainedobjc_retainAutorelease
  • 状态检查:每一次 objc_storeStrong(最常见的赋值操作)都要先判断新旧对象是否相同,然后执行 release 旧值、retain 新值的逻辑。这些判断和分支跳转在极高频的循环中会累积成明显的开销。

2. 弱引用表(Weak Table)的维护

这是 ARC 性能成本最高的环节。

  • 哈希开销:每次创建 __weak 指针,系统都要去全局 SideTable 里通过哈希算法找桶、加锁、插入地址。
  • 清理开销:当对象销毁时,系统必须遍历弱引用数组并逐一置空。如果你有一个被上千个弱引用观察的对象,销毁它时会产生明显的 CPU 尖峰。

3. 阻塞编译器优化

  • 无法内联:由于 objc_retainobjc_release 是动态库(libobjc)里的函数,编译器很难跨模块对这些调用进行内联优化。
  • 寄存器压力:ARC 插入的这些函数调用需要占用寄存器来传递参数,这限制了编译器在复杂算法中对寄存器的优化分配。

4. 异常处理的膨胀

为了确保在代码抛出异常(Exception)时对象依然能正确释放,ARC 必须在某些地方生成额外的异常处理表(Exception Handling Tables)。这不仅增加了二进制文件的大小,也给运行时的栈回溯增加了负担。


既然有成本,为什么还要用?

虽然 ARC 有成本,但它带来的收益通常远超这些微小的性能损耗:

  1. 逻辑优化(ARC 独有的黑科技)

    正如之前提到的 objc_autoreleaseReturnValue,这种“所有权瞬移”优化在 MRC 下很难手动实现。

  2. 避免人为错误

    MRC 时代的内存泄露和野指针崩溃导致的调试成本(和用户流失)远比那 1% 的 CPU 损耗贵得多。

  3. 常态化性能

    手动优化往往只有顶级高手能做到极致。对于 90% 的开发者,ARC 生成的内存管理代码比他们手写的要更高效、更科学。


总结:性能成本对比表

维度MRC (高手手写)ARC
基础 retain/release极低低 (有少量冗余)
弱引用处理手动模拟 (极难且危险)较高 (但极其安全)
所有权转移中等极低 (有编译器优化)
开发效率极高