聊一聊weak

2020-12-11  本文已影响0人  晨阳Xia

1.聊一聊附有__weak修饰符的变量背添加到弱引用表,以及在弱引用表中删除的过程

附有__weak修饰符的变量,指向一个alloc生成的对象,编译之后的代码


image.png

编译前:

{
    id __weak obj1 = obj;
}

编译后:

id obj1;
/*
location:附有__weak修饰符的变量
newObj:被引用的对象
*/

1.

objc_initWeak(id *location, id newObj) {
   if (!newObj) {
        *location = nil
        return nil
   }
   
   return storeWeak<false/*old*/, true/*new*/,  true/*crash*/>(location, (objc_object*)newObj)
}

2.

storeWeak(id *location, id newObj) {
    ...
    SideTable *oldTable;
    SideTable *newTable;
    
    ...
    newTable = &SideTables()[newObj] // 以对象为键值,在散列表中中找到newTable
    
    ...
    newObj = (objc_bjectt)weak_register_no_lock(&newTable->weak_table,(id)newObj,location,CrashIfDellocating)
    
    ...
    // 赋值
    *location = (id)newObj
}

3.
/*
    referent_id:被引用的对象
    *referrer_id:附有__weak修饰符的变量
*/

weak_register_no_lock(weak_table *weak_table, id referent_id, id *referrer_id, bool crashIfDellocating) {
    objc_object *referent = (objc_object *)referent_id;
    objc_object *referrer = (objc_object **)referrer_id;
    
    ...
    weak_entry *entry; // 弱引用数组(理解为数组,其实比数组更复杂)
    if (entry = weak_entry_for_referent(weak_table, referent)) {
        append_referrer(entry, referrer);
    } else { // 没有则创建
        weak_entry_t *new_entry;
        new_entry.referent = referent;
        new_entry.out_of_line = ;
        new_entry.inline_referrer[0] = referrer;
        for (i = 1; i < WEAK_INLINE_COUNT; i++) {
                new_entry.inline_referrer[i] = nil;
        }
    }
    
    weak_grow_maybe(weak_table);
    // 将数组插入散列表中
    weak_insert_entry(weak_table, &new_entry);
}

4.
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent) {
    weak_entry_t weak_entries = weak_table->weak_entries;
    if (!weak_entries) {
        retrun nil;
    }
    
    ...
    
    size_t index = hash_pointer(referent) & weak_table->mash;
    // 哈希冲突
    // 当前索引拿到的对象不是传入referent,则索引加1,往下查找。
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    ...
    return &weak_table->entries[index]
    
    
}

2.objc_initWeak 内部调用 objc_storeWeak函数

obj1 = 0
objc_storeWeak(&obj1, obj)

变量作用域结束时,调用以下函数销毁:

objc_destroyWeak(&obj1, 0);

2.objc_destroyWeak 内部调用 objc_storeWeak函数

objc_storeWeak(&obj1, 0)

2.若附有__weak修饰符的变量所引用的对象被废弃,则将该变量赋值为nil

weak_clear_no_lock(weak_table_t *weak_table, id referent_id){
    objc_object *referent = (objc_object *)referent_id;
    weak_entry_t *entry = weak_entry_for_refetent(weak_table, referent);
    if (!entry) {
        retrun nil;
    }
    weak_referrer_t *referrers;
    size_t count;
    if (entry->out_of_line) {
        referrers = entry->referrers;
        count = TABLE_SIZE(entry)
    } else {
        referrers = entry->inline_referrers;
        count = WEAK_INLINE_COUNT;
    }
    // 将引用对象的所有变量都置为nil
    for (size_t = 0; i < count; i++) {
        objc_object **referrer = referrers[i];
        if (referrer) {
            if (*referrer == referent) {
                *referrer = nil;
            }
        }
    }
    
}

3.使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象

1.使用__weak 修饰符时,一下代码会引起警告:

{
    id __weak obj = [[NSObject alloc] init];
    NSLog(@"obj = %@", obj) // 结果: obj == (null)
}

/* 编译器的模拟代码 */
id obj1;
id tmp = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(tmp, @seletor(init));
objc_initWeak(&obj1, tmp);
objc_release(tmp)
objc_destoryWeak(&obj1)

虽然自己生成并持有的对象通过objc_initWeak函数被赋值给__weak修饰符的变量中,但编译器判断其没有持有者,故该对象立即通过objc_release函数被释放和废弃。

2.使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象

{
    id __weak obj1 = obj;
}
/* 编译器的模拟代码 */
id obj1;
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1)
objc_autorelease(tmp)
objc_destoryWeak(&obj1)

(1)objc_loadWeakRetained函数取出附有__weak修饰符变量所引用的对象并retain
(2) objc_autorelease函数将对象注册到autoreleasepool中
由此可知,因为附有__weak修饰符变量所引用的对象想这样被注册到aotureleasepool中 ,所以在@autoreleasepool块结束之前都可以放心的使用。但是如果大量的使用附有__weak修饰符的变量,注册到autoreleasasepool的对象也会大量的增加.因此在使用附有__weak修饰符的变量时,最好先暂时赋值给附有__strong修饰符的变量后再使用。
图片举例


image.png image.png

独自实现引用计数机制的类大多不支持__weak修饰符。

4.注意

大量使用附有__weak修饰符的变量,会消耗相应的cpu资源

参考文献

上一篇 下一篇

猜你喜欢

热点阅读