美文网首页
dealloc源码解读

dealloc源码解读

作者: Peter杰 | 来源:发表于2019-12-02 11:22 被阅读0次

    isa指针

    在看dealloc源码之前, 首先要了解isa指针

    • 在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址,
    • 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息
    union isa_t {
        isa_t() { }
        isa_t(uintptr_t value) : bits(value) { }
    
        Class cls;
        uintptr_t bits;
    #if defined(ISA_BITFIELD)
        struct {
          uintptr_t nonpointer        : 1;                                       \
          uintptr_t has_assoc         : 1;                                       \
          uintptr_t has_cxx_dtor      : 1;                                       \
          uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
          uintptr_t magic             : 6;                                       \
          uintptr_t weakly_referenced : 1;                                       \
          uintptr_t deallocating      : 1;                                       \
          uintptr_t has_sidetable_rc  : 1;                                       \
          uintptr_t extra_rc          : 19
        };
    #endif
    };
    
    • nonpointer
      1.代表普通的指针,存储着Class、Meta-Class对象的内存地址
      2.代表优化过,使用位域存储更多的信息
    • has_assoc
      是否有设置过关联对象,如果没有,释放时会更快
    • has_cxx_dtor
      是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
    • shiftcls
      存储着Class、Meta-Class对象的内存地址信息
    • magic
      用于在调试时分辨对象是否未完成初始化
    • weakly_referenced
      是否有被弱引用指向过,如果没有,释放时会更快
    • deallocating
      对象是否正在释放
    • extra_rc
      里面存储的值是:引用计数器减1
    • has_sidetable_rc
      1.引用计数器是否过大无法存储在isa中
      2.如果为1,那么引用计数会存储在一个叫SideTable的类的属性中,(如果引用计数19位不够存储的话,会存储在SideTable中)

    引用计数如何存储:

    引用计数可以直接存储在优化过的isa指针中,如果不够的话,会存储在SideTable类中的RefcountMap散列表中去

    struct SideTable {
        spinlock_t slock;
        RefcountMap refcnts;
        weak_table_t weak_table;
    }
    

    引用计数+1(retainCount)的过程

    - (NSUInteger)retainCount {
        return ((id)self)->rootRetainCount();
    }
    
    inline uintptr_t 
    objc_object::rootRetainCount()
    {
        if (isTaggedPointer()) return (uintptr_t)this;  //如果是TaggedPointer指针,不是oc对象
    
        sidetable_lock();
        isa_t bits = LoadExclusive(&isa.bits);
        ClearExclusive(&isa.bits);
        if (bits.nonpointer) {  //是优化过的isa指针
            uintptr_t rc = 1 + bits.extra_rc;  //引用计数+1
            if (bits.has_sidetable_rc) {  //  引用计数不是存储在isa中,而是存储在sidetable中
                rc += sidetable_getExtraRC_nolock();
            }
            sidetable_unlock();
            return rc;
        }
    
        sidetable_unlock();
        return sidetable_retainCount();
    }
    
    size_t 
    objc_object::sidetable_getExtraRC_nolock()
    {
        assert(isa.nonpointer);
        SideTable& table = SideTables()[this];
        RefcountMap::iterator it = table.refcnts.find(this);//获取引用计数列表的遍历器
        if (it == table.refcnts.end()) return 0;
        else return it->second >> SIDE_TABLE_RC_SHIFT;
    }
    

    引用计数-1(retainCount)的过程

    // Replaced by ObjectAlloc
    - (oneway void)release {
        ((id)self)->rootRelease();
    }
    ALWAYS_INLINE bool 
    objc_object::rootRelease()
    {
        return rootRelease(true, false);
    }
    
    ALWAYS_INLINE bool 
    objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
    {
    retry:
        do {
            oldisa = LoadExclusive(&isa.bits);
            newisa = oldisa;
            if (slowpath(!newisa.nonpointer)) {//不是优化过的isa指针
                ClearExclusive(&isa.bits);
                if (sideTableLocked) sidetable_unlock();
                return sidetable_release(performDealloc);
            }
            // don't check newisa.fast_rr; we already called any RR overrides
            uintptr_t carry;
            newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--
            if (slowpath(carry)) {
                // don't ClearExclusive()
                goto underflow;
            }
        } while (slowpath(!StoreReleaseExclusive(&isa.bits, 
                                                 oldisa.bits, newisa.bits)));
    
        if (slowpath(sideTableLocked)) sidetable_unlock();
        return false;
        ......
    }
    
    uintptr_t
    objc_object::sidetable_release(bool performDealloc)
    {
    #if SUPPORT_NONPOINTER_ISA
        assert(!isa.nonpointer);
    #endif
        SideTable& table = SideTables()[this];
    
        bool do_dealloc = false;
    
        table.lock();
        RefcountMap::iterator it = table.refcnts.find(this);
    //引用计数相减操作
        if (it == table.refcnts.end()) {
            do_dealloc = true;
            table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
        } else if (it->second < SIDE_TABLE_DEALLOCATING) {
            // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
            do_dealloc = true;
            it->second |= SIDE_TABLE_DEALLOCATING;
        } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
            it->second -= SIDE_TABLE_RC_ONE;
        }
        table.unlock();
        if (do_dealloc  &&  performDealloc) {//执行dealloc
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
        }
        return do_dealloc;
    }
    

    dealloc执行过程

    当一个对象要释放时,会自动调用dealloc,接下的调用轨迹是
    dealloc
    _objc_rootDealloc
    rootDealloc
    object_dispose
    objc_destructInstance、free

    源码如下

    // Replaced by NSZombies
    - (void)dealloc {
        _objc_rootDealloc(self);
    }
    
    void
    _objc_rootDealloc(id obj)
    {
        assert(obj);
    
        obj->rootDealloc();
    }
    
    inline void
    objc_object::rootDealloc()
    {
        if (isTaggedPointer()) return;  // fixme necessary?
    
        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);
        }
    }
    

    在rootDealloc可以看出,如果对象
    是一个普通的isa指针(isa.nonpointer)
    没有被弱引用指向 (!isa.weakly_referenced)
    没有设置过关联对象(!isa.has_assoc)
    没有C++的析构函数(!isa.has_cxx_dtor)
    没有SideTable(!isa.has_sidetable_rc)
    会直接释放掉free(this);
    如果不满足以上任何一条,会执行 object_dispose

    /***********************************************************************
    * object_dispose
    * fixme
    * Locking: none
    **********************************************************************/
    id 
    object_dispose(id obj)
    {
        if (!obj) return nil;
    
        objc_destructInstance(obj);    
        free(obj);
    
        return nil;
    }
    
    /***********************************************************************
    * objc_destructInstance
    * Destroys an instance without freeing memory. 
    * Calls C++ destructors.
    * Calls ARC ivar cleanup.
    * Removes associative references.
    * Returns `obj`. Does nothing if `obj` is nil.
    **********************************************************************/
    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            // Read all of the flags at once for performance.
            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();  //将指向当前对象的弱指针置为nil
        }
    
        return obj;
    }
    

    object_dispose方法中首先会调用 objc_destructInstance,再释放对象free(obj);
    objc_destructInstance里面会做这些事情:

    1. 判断对象是否有成员变量,如果有,清除成员变量
      bool cxx = obj->hasCxxDtor();
      if (cxx) object_cxxDestruct(obj);
    2. 判断是否有关联对象,如果有,移除关联对象
      bool assoc = obj->hasAssociatedObjects();
      if (assoc) _object_remove_assocations(obj);
    3. 调用 clearDeallocating 将指向当前对象的弱指针置为nil
      obj->clearDeallocating();
    inline void 
    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());
    }
    

    如果不是优化后的isa指针会执行sidetable_clearDeallocating ()
    如果对象被弱引用指向过,或者引用计数存在sidetable中,执行clearDeallocating_slow();

    void 
    objc_object::sidetable_clearDeallocating()
    {
        SideTable& table = SideTables()[this];
    
        // clear any weak table items
        // clear extra retain count and deallocating bit
        // (fixme warn or abort if extra retain count == 0 ?)
        table.lock();
        RefcountMap::iterator it = table.refcnts.find(this);
        if (it != table.refcnts.end()) {
            if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
                weak_clear_no_lock(&table.weak_table, (id)this);
            }
            table.refcnts.erase(it);
        }
        table.unlock();
    }
    
    NEVER_INLINE void
    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();
    }
    

    面试题

    1.week指针实现原理

    将弱引用存储在哈希表里,当对象销毁的时候,将当前对象的弱引用表清除掉

    2.ARC 都帮我们做了什么?

    ARC是LLVM编译器和Runtime系统相互协作的一个结果,LLVM编译器帮我们生成管理相关的代码,Runtime帮我处理弱引用这种操作

    相关文章

      网友评论

          本文标题:dealloc源码解读

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