iOS weak 指针实现原理1

__weak 的作用是什么?

iOS weak 指针实现原理1_第1张图片
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-----
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 对象已经被释放了

iOS weak 指针实现原理1_第2张图片
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)