美文网首页
聊一聊weak

聊一聊weak

作者: 晨阳Xia | 来源:发表于2020-12-11 12:29 被阅读0次

    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资源

    参考文献

    • 《编写高质量iOS与OS X代码的52个有效方法》
    • 《Objective-C高级编程》

    相关文章

      网友评论

          本文标题:聊一聊weak

          本文链接:https://www.haomeiwen.com/subject/oirjgktx.html