2-10.【OC】【内存管理】__unsafe_unretained 与 __weak 的底层差异是什么?

3 阅读2分钟

从底层实现来看,__unsafe_unretained__weak 的差异本质上是 “原始指针”“运行时托管指针” 的区别。

虽然它们都不增加引用计数,但处理对象销毁(Deallocation)的方式完全不同。


1. 底层实现机制的差异

__weak:受 Runtime 托管的“智能指针”

当你声明一个 __weak 变量时,Runtime 会介入并维护一套复杂的账本。

  • 注册机制:调用 objc_initWeak。系统会将该指针的地址注册到全局的 SideTable(哈希表)中。
  • 自动置空(Zeroing) :当指向的对象销毁时,Runtime 会回溯 SideTable,找到所有指向该对象的 __weak 指针,并将它们物理清零(设为 nil)。
  • 读取保护:每次读取 __weak 变量时,底层会调用 objc_loadWeakRetained,临时 retain 对象以确保在读取过程中对象不会突然消失。

__unsafe_unretained:纯粹的 C 语言指针

__unsafe_unretained 几乎不涉及任何 Runtime 逻辑。

  • 无注册:它仅仅是一个地址记录。编译器不会在 SideTable 中留下任何记录。
  • 无清理:当对象销毁时,没有任何机制去通知或修改这个指针。它依然保存着那个已经失效的内存地址。
  • 无保护:读取时直接访问地址,没有任何安全检查。

2. 性能与安全性的博弈

特性__weak__unsafe_unretained
安全性。自动变 nil,防止野指针崩溃。。对象销毁后变成悬挂指针(Dangling Pointer)。
性能开销较高。涉及哈希查找、自旋锁、弱引用表维护。极低。等同于普通 C 指针操作,无额外开销。
适用范围绝大多数循环引用处理场景。极其追求性能的底层算法、或已知生命周期必长于自身的场景。

3. 崩溃的本质原因

这就是为什么它叫 “unsafe”

  • __weak 场景

    obj 销毁 \to 指针变 nil \to 调用 [nil someMethod] \to 安全(Obj-C 支持向 nil 发消息)

  • __unsafe_unretained 场景

    obj 销毁 \to 指针保留旧地址 \to 访问旧地址 \to 发现内存已被系统挪作他用或不可访问 \to EXC_BAD_ACCESS (Crash)


4. 为什么要保留 __unsafe_unretained?

既然 __weak 这么好,为什么不彻底废除 __unsafe_unretained

  1. 性能极致优化:在每秒执行数百万次的小对象操作中,__weak 的锁竞争和哈希查找开销可能成为瓶颈。
  2. 兼容性__weak 机制需要硬件和操作系统的支持(iOS 5+),在极早期的系统或特定的底层 C/C++ 混编环境下,__unsafe_unretained 兼容性更强。
  3. 非对象类型:在某些复杂的内存映射或对非 isa 结构的操作中,Runtime 无法介入。

总结

  • __weak 是有**“售后服务”**的:它在后台偷偷帮你擦屁股,确保你不会因为对象消失而摔倒。
  • __unsafe_unretained 是**“后果自负”**的:它只管记录地址,至于那个地址现在是金矿还是地雷,它一概不理。