weak表

312 阅读1分钟

初始化:

第一层 在weak_table中查找weak_entry:

__weak修饰后,会调用 storeWeak(id *location, objc_object *newObj) 这个方法。 location 为weak指针地址,newObj是被弱引用的对象。 然后调用weak_register_no_lock这个方法。  这里为最外层的hash查找,KEY为referent 即被引用的对象,value为weak_entry表,如果没有就创建。  

  if (haveNew) {        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // weak_register_no_lock returns nil if weak store should be rejected

        // Set is-weakly-referenced bit in refcount table.
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }
        // Do not set *location anywhere else. That would introduce a race.
        *location = (id)newObj;
    }

weak_register_no_lock(weak_table_t *weak_table, id referent_id, 
                      id *referrer_id, bool crashIfDeallocating){
    objc_object *referent = (objc_object *)referent_id;
    objc_object **referrer = (objc_object **)referrer_id;

 weak_entry_t *entry;
    if ((entry = weak_entry_for_referent(weak_table, referent))) {
        append_referrer(entry, referrer);
    } 
    else {
        weak_entry_t new_entry(referent, referrer);
        weak_grow_maybe(weak_table);
        weak_entry_insert(weak_table, &new_entry);
    }

第二层 在entry表里插入referrer: 

然后添加referrer到entry里面的数组里面,如果inline_referrers有空的,添加到inline_referrers里面。否则添加referrers数组里面。这里第二次用到哈希查找算法。

new_referrer  为 二级指针,  *new_referrer 指向 指向newObj的指针 根据 new_referrer  w_hash_pointer(new_referrer) & (entry->mask)  找出 referrers[index]为nil时候的index,  然后令  ref = new_referrer

 size_t begin = w_hash_pointer(new_referrer) & (entry->mask);

size_t index = begin;

size_t hash_displacement = 0;

while (entry->referrers[index] != nil) {

hash_displacement++;

index = (index+1) & entry->mask;

if (index == begin) bad_weak_table(entry);

}

if (hash_displacement > entry->max_hash_displacement) {

entry->max_hash_displacement = hash_displacement;

}

weak_referrer_t &ref = entry->referrers[index];

ref = new_referrer;

entry->num_refs++;

调用完 register方法后, 

 *location = (id)newObj 

使二级指针 所指向的指针  *new_referrer 指向 newObj

所以当weak表清空时,  先遍历出来所有的referrers, 然后置空的为一级指针 *referrer = nil;

weak_clear_no_lock(weak_table_t *weak_table, id referent_id)

for (size_t i = 0; i < count; ++i) {

objc_object **referrer = referrers[i];

if (referrer) {

if (*referrer == referent) {

*referrer = nil;

}

但是删除单个weak引用时,

remove_referrer(weak_entry_t *entry, objc_object **old_referrer)

 entry->referrers[index] = nil;

这里是单个二级指针置为空