weak 引用在 iOS 中通过维护一个全局的弱引用表来实现。当弱引用的对象被释放时,所有指向它的弱引用会被自动置为 nil,从而防止悬挂指针。
弱引用表(Weak Table)的键和值
理解弱引用表的键和值对于理解
weak
引用的底层机制非常重要。下面我详细解释一下这两个概念,并用示例和图表来说明。
键(Key)
- 对象指针(Object Pointer):这是被
weak
引用的对象的内存地址。每个被weak
引用的对象在弱引用表中都有一个对应的条目,其键就是这个对象的内存地址。
值(Value)
- 弱引用指针集合(Set of Weak Reference Pointers):这是一个集合,包含了所有指向该对象的
weak
引用指针的地址。当一个对象有多个weak
引用时,这些引用指针的地址都会记录在集合中。
具体示例
示例代码
Person *personInstance = [[Person alloc] init];
__weak Person *weakPerson1 = personInstance;
__weak Person *weakPerson2 = personInstance;
在这个示例中:
personInstance
是一个Person
对象的强引用。weakPerson1
和weakPerson2
是Person
对象的两个弱引用。
弱引用表表示
- 创建弱引用时:
- 假设
personInstance
的内存地址是0x1000
。 weakPerson1
的内存地址是0x2000
。weakPerson2
的内存地址是0x3000
。
- 假设
Weak Table:
+-------------------+-------------------+
| Object Pointer | Weak Reference(s) |
+-------------------+-------------------+
| 0x1000 | [0x2000, 0x3000] | // personInstance is referenced by weakPerson1 and weakPerson2
+-------------------+-------------------+
在这个弱引用表中:
- 键
0x1000
是personInstance
的内存地址。 - 值
[0x2000, 0x3000]
是一个集合,包含了所有指向personInstance
的弱引用指针(weakPerson1
和weakPerson2
的地址)。
对象释放时
当 personInstance
的引用计数变为零,系统准备释放该对象时,运行时会执行以下操作:
-
找到所有弱引用:
- 在弱引用表中查找键
0x1000
,找到对应的值[0x2000, 0x3000]
。
- 在弱引用表中查找键
-
置
nil
:- 将
0x2000
和0x3000
地址上的值置为nil
。
- 将
-
删除条目:
- 从弱引用表中删除键
0x1000
的条目。
- 从弱引用表中删除键
Weak Table (before release):
+-------------------+-------------------+
| Object Pointer | Weak Reference(s) |
+-------------------+-------------------+
| 0x1000 | [0x2000, 0x3000] |
+-------------------+-------------------+
Weak Table (after release):
+-------------------+-------------------+
| Object Pointer | Weak Reference(s) |
+-------------------+-------------------+
| (nil) | [nil, nil] | // personInstance 已被释放,weakPerson1 和 weakPerson2 被置为 nil
+-------------------+-------------------+
关键函数
在实现弱引用机制时,运行时系统使用以下关键函数:
libobjc
中的一系列 API
objc_initWeak
:初始化一个弱引用,将其添加到弱引用表中。objc_loadWeak
:读取一个弱引用的值,确保在对象被释放后返回nil
。objc_storeWeak
:给弱引用赋值,并更新弱引用表。objc_destroyWeak
:销毁一个弱引用,并从弱引用表中移除对应的条目。
概述
弱引用表的键是被引用对象的内存地址,而值是一个集合,包含了所有指向该对象的弱引用指针的地址。当对象被释放时,运行时会在弱引用表中找到所有指向该对象的弱引用,并将它们置为 nil
,然后删除对应的条目。通过这种机制,iOS 保证了 weak
引用的安全性和可靠性。