基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

__weak 和 __unsafe_unretained 的区别?

知识点图片

__weak__unsafe_unretained 都是 iOS 开发中 ARC(自动引用计数)环境下用于修饰弱引用的关键字。它们共同点是:都不会增加所指向对象的引用计数

但是,它们在对象被销毁(dealloc)后的行为上有本质的区别。

以下是详细对比:

1. 核心区别:对象销毁后的行为

  • __weak (自动置 nil)

    • 当所指向的对象被释放(dealloc)时,ARC 运行时会自动将 __weak 修饰的指针置为 nil
    • 结果:向 nil 发送消息是安全的(什么都不会发生),不会导致崩溃。
    • 形象比喻:你牵着一条狗(对象),绳子松了(weak)。狗跑了(销毁),你手里的绳子会自动消失,你手里什么都没有了。
  • __unsafe_unretained (悬垂指针/野指针)

    • 当所指向的对象被释放时,__unsafe_unretained 修饰的指针保持原状,依然指向那个已经被回收的内存地址。
    • 结果:这个指针变成了“悬垂指针”或“野指针”。如果你再次尝试访问这个指针,程序会崩溃(通常报 EXC_BAD_ACCESS)。
    • 形象比喻:你牵着一条狗,狗跑了(销毁),但你手里还死死拽着那根绳子,指着那个空荡荡的地方。如果你试着去摸那只已经不存在的狗,你会被咬(崩溃)。

2. 底层实现与性能

  • __weak

    • 实现原理:Runtime 维护了一张全局的 SideTables(哈希表)。当一个对象被 __weak 指针引用时,Runtime 会将这个指针的地址注册到表中。当对象销毁时,Runtime 会去查表,找到所有指向该对象的 __weak 指针,并将它们全部置为 nil
    • 性能:由于涉及到哈希表的查表、插入、删除和加锁操作,性能开销相对较大
  • __unsafe_unretained

    • 实现原理:仅仅是一个指针赋值操作,Runtime 不做任何额外的跟踪。
    • 性能性能非常高,几乎等同于 C 语言的原始指针赋值。

3. 使用场景

  • __weak

    • 首选方案
    • 用于解决循环引用(Retain Cycle),例如在 Block 中引用 self,或者 Delegate 模式。
    • 适用于 UI 控件的 IBOutlet(虽然现在强引用也可以,但习惯上常用 weak)。
  • __unsafe_unretained

    • 极少使用,除非有极端的性能优化需求。
    • 用于对象生命周期非常明确,且你能 100% 保证指针的生命周期短于对象的生命周期(即访问指针时对象一定还在)。
    • 在 iOS 4 时代(ARC 刚出但不支持 weak)作为兼容方案使用(现在已无此需求)。
    • 某些特定的 C 结构体或 Core Foundation 类型转换时可能用到。

4. 代码示例对比

使用 __weak

plaintext
id obj = [[NSObject alloc] init];
__weak id weakPtr = obj;

NSLog(@"1. weakPtr: %@", weakPtr); // 输出对象地址

// 释放对象
obj = nil; 

// 再次访问
NSLog(@"2. weakPtr: %@", weakPtr); // 输出 (null),安全

使用 __unsafe_unretained

plaintext
id obj = [[NSObject alloc] init];
__unsafe_unretained id unsafePtr = obj;

NSLog(@"1. unsafePtr: %@", unsafePtr); // 输出对象地址

// 释放对象
obj = nil; 

// 再次访问
// NSLog(@"2. unsafePtr: %@", unsafePtr); // 💥 必崩!访问了非法内存 (EXC_BAD_ACCESS)

总结

特性 __weak __unsafe_unretained
对象销毁后 指针自动变为 nil 指针保持原地址(野指针)
安全性 高(安全) 低(不安全,易崩溃)
性能 较低(依赖 Runtime 维护 SideTable) 极高(无额外开销)
对应属性修饰符 @property (weak) @property (assign) (用于对象时)
推荐程度 强烈推荐 尽量避免
00:00
00:00