iOS 在运行时维护着一个全局的弱引用表,该表是一个 hash 表,hash表的 key 是 对象本身,value 是指向该对象的所有 weak 指针的地址数组。
/**
全局的弱引用表,本质是一个hash结构,对象本身作为key,
存储weak修饰的指针地址的weak_entry_t作为value
*/
struct weak_table_t {
// 保存了所有指向特地对象的 weak指针集合
weak_entry_t *weak_entries;
// hash数组中元素个数
size_t num_entries;
// hash数组的长度,而不是元素个数。比如,数组长度可能是64,而元素个数仅存了2个
uintptr_t mask;
// hash冲突最大次数, hash数组采用开放定址法解决hash冲突
uintptr_t max_hash_displacement;
};
以下述代码为例:
NSObject * obj = [[NSObject alloc] init];
__weak NSObject *p1 = obj;
__weak NSObject *p2 = obj;
NSObject ** referrer1 = &p1;
NSObject ** referrer2 = &p2;
[obj release];
hash表的key为obj,hash表的值weak_entries的referers属性被赋值为[referrer1, referrer2];
当weak修饰的对象obj被销毁时,iOS在运行时会从哈希表中查找到所有指向此对象的 weak 指针,并将其全部置为空 nil,即通过执行*referrer1 = NULL和*referrer2 = NULL,实现将p1和p2置为NULL。
weak_clear_no_lock(weak_table_t *weak_table, id referent_id) {
// 在weak_table中对应的weak_entry_t
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
weak_referrer_t referrers = entry->referrers;
int count = TABLE_SIZE(entry);
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
// 如果weak指针确实弱引用了对象 referent,则将weak指针设置为nil
if (*referrer == referent) {
*referrer = nil;
}
}
}
}
iOS在ARC下通过引入weak标识, 大大减少了以前retain或assign标识的对象在被销毁后可能出现野指针的情况,进而有效提升了代码健壮性。