美文网首页
IOS基础知识-weak原理篇

IOS基础知识-weak原理篇

作者: 程序员的自我救赎 | 来源:发表于2019-06-24 16:27 被阅读0次
    weak的作用是弱引用,修饰的对象引用计数不会+1;在对象被释放后,对象会被置为nil;
    

    weak底层原理

    首先需要看下weak编译后的实现,比如有如下代码:

    int main(){
        NSObject *obj = [[NSObject alloc] init];
        id __weak obj1 = obj;
    }
    

    在通过clang编译后,代码实现如下:

    int main(){
        NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
        id __attribute__((objc_ownership(weak))) obj1 = obj;
    }
    

    编译后的weak,通过objc_ownership实现weak方法:即获取对象所有权,是对对象weak初始化的一个操作

    weak的实现原理:

    由上可知,weak是基于runtime来实现的(objc_msgSend);
    weak和strong都是Objective-C的修饰符,strong是由runtime维护的一个自动计数表结构,而weak是有runtime维护的weak表;
    在runtime的源码中,可以找到objc_weak.h和objc_weak.m文件;而且在objc_weak.h的头文件中可以找到weak表的结构定义
    

    weak表

    下面的代码是runtime中的定义:

    /**
     * The global weak references table. Stores object ids as keys,
     * and weak_entry_t structs as their values.
     */
    struct weak_table_t {
        weak_entry_t *weak_entries; //保存了所有指向指定对象的weak指针   weak_entries的对象
        size_t    num_entries;              // weak对象的存储空间
        uintptr_t mask;                      //参与判断引用计数辅助量
        uintptr_t max_hash_displacement;    //hash key 最大偏移值
    };
    

    weak_table_t 是一个全局的weak引用表,使用不定类型的地址作为key,weak_entry_t 作为value,是一个存储在弱引用表中的内部结构体,它负责维护和存储指向一个对象的所有弱引用的hash表;其定义如下:

    typedef objc_object ** weak_referrer_t;
    struct weak_entry_t {
        DisguisedPtr<objc_object> referent;  //范型
        union {
            struct {
                weak_referrer_t *referrers;
                uintptr_t        out_of_line : 1;
                uintptr_t        num_refs : PTR_MINUS_1;
                uintptr_t        mask;
                uintptr_t        max_hash_displacement;
            };
            struct {
                // out_of_line=0 is LSB of one of these (don't care which)
                weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
            };
        }
    }
    

    在 weak_entry_t 的结构中,DisguisedPtr referent 是对泛型对象的指针做了一个封装,通过这个泛型类来解决内存泄漏的问题。从注释中写 out_of_line 成员为最低有效位,当其为0的时候, weak_referrer_t 成员将扩展为多行静态 hash table。其实其中的 weak_referrer_t 是二维 objc_object 的别名,通过一个二维指针地址偏移,用下标作为 hash 的 key,做成了一个弱引用散列。

    out_of_line:最低有效位,也是标志位。当标志位 0 时,增加引用表指针纬度。
    num_refs:引用数值。这里记录弱引用表中引用有效数字,因为弱引用表使用的是静态 hash 结构,所以需要使用变量来记录数目。
    mask:计数辅助量。
    max_hash_displacement:hash 元素上限阀值。
    其实 out_of_line 的值通常情况下是等于零的,所以弱引用表总是一个 objc_objective 指针二维数组。一维 objc_objective 指针可构成一张弱引用散列表,通过第三纬度实现了多张散列表,并且表数量为 WEAK_INLINE_COUNT 。
    

    总之:

    weak_table_t:weak全局表,采用hash(哈希表)的方式存储weak引用对象
    weak_entry_t:weak引用对象,即weak_table_t中hash表的value
    objc_object:标记weak对象,即weak_entry_t中的泛型对象
    

    weak的释放为nil的过程

    a、调用objc_release释放
    b、因为引用计数为0,则调用对象的dealloc->objc_rootDealloc->objc_dispose
    c、调用objc_destructInstance
    d、最后调用objc_clear_deallocating
    objc_clear_deallocating该函数的动作如下:
    1、从weak表中获取废弃对象的地址为键值的记录
    2、将包含在记录中的所有附有 weak修饰符变量的地址,赋值为nil
    3、将weak表中该记录删除
    4、从引用计数表中删除废弃对象的地址为键值的记录
    其实Weak表是一个hash(哈希)表,然后里面的key是指向对象的地址,Value是Weak指针的地址的数组。
    

    总结

    weak是Runtime维护了一个hash(哈希)表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。
    1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
    2、添加引用时:objc_initWeak函数会调用 storeWeak() 函数, storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
    3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

    相关文章

      网友评论

          本文标题:IOS基础知识-weak原理篇

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