iOS weak 指针实现原理1

作者: 大兵布莱恩特 | 来源:发表于2019-02-20 12:31 被阅读22次

    __weak 的作用是什么?

    image.png

    先给出一段代码 分别用 __strong __weak __unsafe_unretained 指向这个Persion 对象时控制台输出内容分别是什么?

    __strong 指向该 Person 对象时 最后Person 才被 dealloc 说明 __strong是个强引用指针 Preson 对象在作用域结束后并没有被立即释放掉

    2019-02-19 16:27:04.909648+0800 objc-debug[25362:21505212] 111111111111
    2019-02-19 16:27:04.910063+0800 objc-debug[25362:21505212] 22222222222-----<Person: 0x100e46a20>
    2019-02-19 16:27:04.910085+0800 objc-debug[25362:21505212] Person dealloc
    

    __weak 指向该 Person 对象时 Person 在方法作用域结束时就被释放掉 __weak是一个弱指针 同时打印 p2指针地址 为 null

    2019-02-19 16:29:39.860058+0800 objc-debug[26138:21512702] 111111111111
    2019-02-19 16:29:39.860354+0800 objc-debug[26138:21512702] Person dealloc
    2019-02-19 16:29:39.860384+0800 objc-debug[26138:21512702] 22222222222-----(null)
    
    

    __unsafe_unretained 指向该 Person 对象时 Person 在方法作用域结束时就被释放掉 __unsafe_unretained也是一个弱指针 同时打印 p2指针地址 不为 null ,如果在后续使用到 p3 这个指针时候 会出现坏内存访问 因为这个指针指向的P erson 对象已经被释放了

    2019-02-19 16:29:39.860058+0800 objc-debug[26138:21512702] 111111111111
    2019-02-19 16:29:39.860354+0800 objc-debug[26138:21512702] Person dealloc
    2019-02-19 16:29:39.860384+0800 objc-debug[26138:21512702] 22222222222-----(null)
    
    

    通过上边的例子可以得出结论 __weak指向对象释放后 指针会置为 nil 避免了成为野指针造成坏内存访问

    runtime源码是如何做到在对象释放时候将__weak修饰 指针置为 nil 的

    源码部分来自苹果官方objc4_723
    阅读源码顺序如下

    1 (NSObject.mm)  _objc_rootDealloc(id obj)
    2 (objc-object.h)   inline void objc_object::rootDealloc()
    3 (objc-runtime-new.mm)   id object_dispose(id obj)
    4 (objc-runtime-new.mm)   void *objc_destructInstance(id obj) 
    5 (objc-object.h)  objc_object::clearDeallocating()
    6 (NSObject.mm) objc_object::clearDeallocating_slow()
    7 (objc-weak.mm) weak_clear_no_lock(weak_table_t *weak_table, id referent_id) 
    

    objc_object::rootDealloc()

    首先判断对象 dealloc 时满足快速释放条件 , 当这个对象没有关联属性 __weak 指针指向 没有C++析构函数 等条件时 可以直接 free 释放 ,否则就进行下一步处理释放的工作

    {
        if (isTaggedPointer()) return;  // fixme necessary?
    
        /* nonpointer 0 代表普通的指针 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
         1,代表优化过,使用位域存储更多的信息
         has_assoc 是否有关联对象 如果没有释放会很快
         has_cxx_dtor 是否有C++析构函数 如果没有释放会更快
         weakly_referenced 释放有被弱引用指向 如果没有 释放会更快
         has_sidetable_rc 引用计数器是否过大无法存储在isa中
         如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
        */
        /// 快速释放对象 满足上边的条件
        if (fastpath(isa.nonpointer  &&
                     !isa.weakly_referenced  &&  
                     !isa.has_assoc  &&  
                     !isa.has_cxx_dtor  &&  
                     !isa.has_sidetable_rc))
        {
            assert(!sidetable_present());
            free(this);
        }  else { ///不能够快速是否 需要进一步做释放处理
            object_dispose((id)this);
        }
    }
    
    

    id object_dispose(id obj)

    id 
    object_dispose(id obj)
    {
        if (!obj) return nil;
        /// 释放对象前 要将对象的关联属性 weak 指针置空 修改引用计数 isa 指针的一些信息
        objc_destructInstance(obj);    
        free(obj);
    
        return nil;
    }
    
    
    /***********************************************************************
     * objc_destructInstance
     *在不释放内存的情况下销毁实例。
     调用c++析构函数。
     *调用ARC ivar清除。
     删除关联引用。
     *返回的obj。如果obj为nil,则什么也不做。
    **********************************************************************/
    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            // Read all of the flags at once for performance.
            /// 是否有C++析构函数
            bool cxx = obj->hasCxxDtor();
            /// 是否有关联属性
            bool assoc = obj->hasAssociatedObjects();
    
            // This order is important. 这个顺序很重要
            if (cxx) object_cxxDestruct(obj); /// 如果有析构函数 去处理下
            if (assoc) _object_remove_assocations(obj); ///如果有关联的属性 那么就移除掉所有关联属性
            ///明确回收
            obj->clearDeallocating();
        }
    
        return obj;
    }
    
    
    

    clearDeallocating 满足慢释放条件

    objc_object::clearDeallocating()
    {
        if (slowpath(!isa.nonpointer)) {
            // Slow path for raw pointer isa.
            sidetable_clearDeallocating();
        } else if (slowpath(isa.weakly_referenced  ||  isa.has_sidetable_rc)) {
            // Slow path for non-pointer isa with weak refs and/or side table data.
            clearDeallocating_slow();
        }
    
        assert(!sidetable_present());
    }
    
    

    clearDeallocating_slow() 指针能讲 weak 指针置为 nil 的地方

    objc_object::clearDeallocating_slow()
    {
        assert(isa.nonpointer  &&  (isa.weakly_referenced || isa.has_sidetable_rc));
    
        SideTable& table = SideTables()[this];
        table.lock(); ///加锁
        if (isa.weakly_referenced) { /// 有弱引用指向
            weak_clear_no_lock(&table.weak_table, (id)this);
        }
        if (isa.has_sidetable_rc) {
            table.refcnts.erase(this);
        }
        table.unlock(); ///解锁
    }
    

    下一篇我会单独去讲解 SideTable 里边 weak_table 具体做了些什么

    iOS weak 指针实现原理2

    好了,我是大兵布莱恩特,欢迎加入博主技术交流群,iOS 开发交流群

    QQ20180712-0.png

    相关文章

      网友评论

        本文标题:iOS weak 指针实现原理1

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