iOS weak 原理

作者: 学不来的凡人 | 来源:发表于2021-03-28 08:26 被阅读0次

    一、weak 基本用法
    weak 是弱引用,用 weak 来修饰、描述所引用对象的计数器并不会增加,而且 weak 会在引用对象被释放的时候自动置为 nil,这也就避免了野指针访问坏内存而引起奔溃的情况,另外 weak 也可以解决循环引用。

    拓展:为什么修饰代理使用 weak 而不是用 assign?

    assign 可用来修饰基本数据类型,也可修饰 OC 的对象,但如果用 assign 修饰对象类型指向的是一个强指针,当指向的这个指针释放之后,它仍指向这块内存,必须要手动给置为 nil,否则会产生野指针,如果还通过此指针操作那块内存,会导致 EXC_BAD_ACCESS 错误,调用了已经被释放的内存空间;而 weak 只能用来修饰 OC 对象,而且相比 assign 比较安全,如果指向的对象消失了,那么它会自动置为 nil,不会导致野指针

    二、weak 原理概括

    weak 表其实是一个哈希表,key 是所指对象的指针,value 是 weak 指针的地址数组。(value 是数组的原因是:因为一个对象可能被多个弱引用指针指向)

    Runtime 维护了一张 weak 表,用来存储某个对象的所有的 weak 指针。

    weak 原理实现过程三步骤

    初始化开始时,会调用 objc_initWeak 函数,初始化新的 weak 指针指向对象的地址。


    image.jpeg image.png
    然后 objc_initWeak 函数里面会调用 objc_storeWeak() 函数,objc_storeWeak() 函数的作用是用来更新指针的指向,创建弱引用表。 image.jpeg image.png

    在最后会调用 clearDeallocating 函数。而clearDeallocating 函数首先根据对象的地址获取 weak 指针地址的数组,然后紧接着遍历这个数组,将其中的数组开始置为 nil,把这个 entry 从 weak 表中删除,最后一步清理对象的记录。

    初始化开始时,会调用 objc_initWeak 函数,初始化新的 weak 指针指向对象的地址。当我们初始化 weak 变量时,runtime 会调用 NSObject.mm 中的 objc_initWeak,而 objc_initWeak 函数里面的实现如下:

    id objc_initWeak(id *location, id newObj) {
    // 查看对象实例是否有效,无效对象直接导致指针释放
        if (!newObj) {
            *location = nil;
            return nil;
        }
        // 这里传递了三个 Bool 数值
        // 使用 template 进行常量参数传递是为了优化性能
        return storeWeakfalse/*old*/, true/*new*/, true/*crash*/>
        (location, (objc_object*)newObj);
    }
    
    

    通过上面代码可以看出,objc_initWeak()函数首先判断指针指向的类对象是否有效,无效,直接返回;否则通过 storeWeak() 被注册为一个指向 value 的 _weak 对象

    objc_initWeak 函数里面会调用 objc_storeWeak() 函数,objc_storeWeak() 函数的作用是用来更新指针的指向,创建弱引用表。

    在最后会调用 clearDeallocating 函数。而 clearDeallocating 函数首先根据对象的地址获取 weak 指针地址的数组,然后紧接着遍历这个数组,将其中的数组开始置为 nil,把这个 entry 从 weak 表中删除,最后一步清理对象的记录。

    问:当 weak 指向的对象被释放时,如何让 weak 指针置为 nil 的呢?
    答:

    调用 objc_release
    因为对象的引用计数为0,所以执行 dealloc
    在 dealloc 中,调用了 _objc_rootDealloc 函数
    在 _objc_rootDealloc 中,调用了 object_dispose 函数
    调用 objc_destructInstance
    最后调用 objc_clear_deallocating,详细过程如下:
    a. 从 weak 表中获取废弃对象的地址为键值的记录
    b. 将包含在记录中的所有附有 weak 修饰符变量的地址,赋值为 nil
    c. 将 weak 表中该记录删除
    d. 从引用计数表中删除废弃对象的地址为键值的记录

    相关文章

      网友评论

        本文标题:iOS weak 原理

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