weak的实现原理

时间:2024-04-16 08:27:32
         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标识的对象在被销毁后可能出现野指针的情况,进而有效提升了代码健壮性。