iOS 在运行时维护着一个全局的弱引用表,该表是一个 hash 表,hash表的 key 是 weak 对象的地址,value 是指向该对象的所有 weak 指针的地址数组。
/**全局的弱引用表,本质是一个hash结构,object作为key, 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冲突uintptr_t max_hash_displacement;
};
以下述代码为例:
NSObject * obj = [[NSObject alloc] init];__weak NSObject *p1 = obj;
__weak NSObject *p2 = obj;objc_object ** referrer1 = &p1;
objc_object ** 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_tobjc_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指针设置为nilif (*referrer == referent) { *referrer = nil;}}}
}
iOS在ARC下通过引入weak标识, 大大减少了以前retain或assign标识的对象在被销毁后可能出现野指针的情况,进而有效提升了代码健壮性。