美文网首页
Retain 和 Release

Retain 和 Release

作者: forping | 来源:发表于2020-11-27 15:13 被阅读0次

    属性的setter和getter方法 中,我们可以发现, objc_retain 方法被调用.
    那么对象的引用计数管理,是怎么进行的呢?

    - (void)testWeak{    
        NSObject *objct = [[NSObject alloc] init];
        NSObject *o2 = objct;
        o2 = nil;
    }
    

    arm64 汇编

    "-[A testWeak]":                        ; @"\01-[A testWeak]"
        sub sp, sp, #80             ; =80 ; sp = sp - 80
        stp x29, x30, [sp, #64]     ; 16-byte Folded Spill  将 x29 和 x30 的值存储到 sp+64
        add x29, sp, #64            ; =64  x29 = sp+64
        stur    x0, [x29, #-8] ;  sp + 56  = x0 = self
        stur    x1, [x29, #-16]  sp + 48  =  x1 = SEL
        adrp    x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGE  ;  加载 类对象列表  base地址,存储到x8
        add x8, x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGEOFF ;  根据偏移量计算 NSObject 的类对象地址, 地址 x8 = x8 + 偏移地址
        ldr x0, [x8] ;  x0 = x8;
        bl  _objc_alloc ; 调用  id _Nullable objc_alloc(Class _Nullable cls) 方法
        adrp    x8, _OBJC_SELECTOR_REFERENCES_@PAGE; 加载方法列表  base地址,存储到x8
        add x8, x8, _OBJC_SELECTOR_REFERENCES_@PAGEOFF ; 根据偏移量计算 NSObject 的 -(instancetype)init 方法地址, 地址 x8 = x8 + 偏移地址
        ldr x1, [x8]  ; -(instancetype)init 方法放到x1
        bl  _objc_msgSend  ; 调用init方法, 
        sub x8, x29, #24            ; =24; x8 = sp+40;
        stur    x0, [x29, #-24] ; x0 存储到  sp+40  = object,对应了 NSObject *objct = [[NSObject alloc] init]; 代码
        ldur    x0, [x29, #-24] ; sp+40存储到x0  = object
        str x8, [sp, #24]           ; 8-byte Folded Spill , x8 放到  sp + 24 = object
    
        bl  _objc_retain ;  调用 _objc_retain 方法, 类似 [objct retain]; 此时引用计数器的值为 2
    
        add x8, sp, #32             ; =32   x8 = sp+32
    
        str x0, [sp, #32] ; x0 放到 sp + 32  = object 对应NSObject *o2 = objct; 代码
    
        mov x0, x8 ; x0 = x8 = sp+32 = o2;
        mov x9, #0; x9 = 0  
        mov x1, x9; x1 = 0; 
        str x8, [sp, #16]           ; 8-byte Folded Spill ; sp+16 = x0 = o2
        str x9, [sp, #8]            ; 8-byte Folded Spill sp+8 = x9 = 0;
    
        bl  _objc_storeStrong ;  调用 void objc_storeStrong(id *location, id obj)方法  对应了o2 = nil; 方法
    
        ldr x0, [sp, #16]           ; 8-byte Folded Reload  x0 = sp + 16; = o2
        ldr x1, [sp, #8]            ; 8-byte Folded Reload  x1 = sp+8 = 0; 释放 o2
    
        bl  _objc_storeStrong ; 作用域结束, object = nil
    
        ldr x0, [sp, #24]           ; 8-byte Folded Reload  x0 = sp + 24 = object;
        ldr x1, [sp, #8]            ; 8-byte Folded Reload x1 = sp + 8 = 0;
    
        bl  _objc_storeStrong ; 释放对象
    
        ldp x29, x30, [sp, #64]     ; 16-byte Folded Reload
        add sp, sp, #80             ; =80
        ret
    

    可以看到

    NSObject对象的创建调用了 objc_alloc 函数

    关于对象的内存分配和类的关联可以看下面的文章
    https://www.jianshu.com/p/532ddbf803a2

    引用计数的管理,主要体现在objc_retain``objc_storeStrong方法中,
    可能会想, 对象刚创建出来,引用计数为1, 那么怎么赋值的呢, 但其实是因为引用计数器存储的值是引用计数器减1

    在目前最新的源码,818.2中,创建对象后引用计数会赋值为1,引用计数器存储的值直接就是引用计数了

    objc_retain 和 retain

    objc_retain
    id objc_retain(id obj)
    {
        if (!obj) return obj;
        if (obj->isTaggedPointer()) return obj;
        return obj->retain();
    }
    
    id objc_object::retain()
    {
        ASSERT(!isTaggedPointer());
    
        return rootRetain(false, RRVariant::FastOrMsgSend);
    }
    

    rootRetain 这个函数来增加引用计数

    ALWAYS_INLINE id
    objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
    {
        if (slowpath(isTaggedPointer())) return (id)this;
    
        bool sideTableLocked = false;
        bool transcribeToSideTable = false;
    
        isa_t oldisa;
        isa_t newisa;
    
        oldisa = LoadExclusive(&isa.bits);
    
        if (variant == RRVariant::FastOrMsgSend) { // 主要检查是否需要调用objc_msgSend 
            // These checks are only meaningful for objc_retain()
            // They are here so that we avoid a re-load of the isa.
            if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) { // 如果具有默认的retain/release/autorelease/方法
                ClearExclusive(&isa.bits);
                if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
                    return swiftRetain.load(memory_order_relaxed)((id)this);
                }
                return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));
            }
        }
    
        if (slowpath(!oldisa.nonpointer)) { 
            // a Class is a Class forever, so we can perform this check once
            // outside of the CAS loop
            if (oldisa.getDecodedClass(false)->isMetaClass()) { //检查isa是不是元类对象,
                ClearExclusive(&isa.bits);
                return (id)this;
            }
        }
    
        do {
            transcribeToSideTable = false;
            newisa = oldisa; // 创建一个临时的isa:newisa,操作都是针对临时变量来操作,最后将newisa的bits信息存储到原始的isa中去,有个do-while去处理存储的逻辑,失败的话就重试
            if (slowpath(!newisa.nonpointer)) { // 不是优化过的指针,直接使用sidetable存储计数
                ClearExclusive(&isa.bits);
                if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
                else return sidetable_retain(sideTableLocked);
            }
            // don't check newisa.fast_rr; we already called any RR overrides
            if (slowpath(newisa.isDeallocating())) { // 如果当前正在释放
                ClearExclusive(&isa.bits);
                if (sideTableLocked) {
                    ASSERT(variant == RRVariant::Full);
                    sidetable_unlock();
                }
                if (slowpath(tryRetain)) { // 直接返回 不增加引用计数
                    return nil;
                } else {
                    return (id)this;
                }
            }
            uintptr_t carry;
            newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++ 增加引用计数, carry表示数值是否溢出
    
            if (slowpath(carry)) {
                // newisa.extra_rc++ overflowed
                if (variant != RRVariant::Full) { // 如果溢出,且当前不是溢出状态,就设置为溢出状态,并且重新调用方法
                    ClearExclusive(&isa.bits);
                    return rootRetain_overflow(tryRetain);
                }
                // Leave half of the retain counts inline and 
                // prepare to copy the other half to the side table.
                if (!tryRetain && !sideTableLocked) sidetable_lock();
                sideTableLocked = true;
                transcribeToSideTable = true;
                newisa.extra_rc = RC_HALF; // 引用计数减为一半
                newisa.has_sidetable_rc = true; // 有引用计数在sidetable里存储
            }
        } while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));
    
        if (variant == RRVariant::Full) { // 如果是满的,且  transcribeToSideTable == true
            if (slowpath(transcribeToSideTable)) {
                // Copy the other half of the retain counts to the side table.
                sidetable_addExtraRC_nolock(RC_HALF); // 存储到sidetable
            }
    
            if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
        } else {
            ASSERT(!transcribeToSideTable);
            ASSERT(!sideTableLocked);
        }
    
        return (id)this;
    }
    

    sidetable_addExtraRC_nolock

    bool  objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
    {
        ASSERT(isa.nonpointer); 
        SideTable& table = SideTables()[this];
        // 获取当前的引用计数
        size_t& refcntStorage = table.refcnts[this];
        size_t oldRefcnt = refcntStorage;
        // isa-side bits should not be set here
        ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0); // 正在释放
    
        ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0); // 弱引用正在释放
        // 系统计数极限了,直接true
        if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
    
        uintptr_t carry;
    // 增加引用计数
        size_t newRefcnt = 
            addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
        if (carry) {
            // 如果借位保存在这里还溢出,就当做SIDE_TABLE_RC_PINNED次数(32或64最大位数-1的数值)
            refcntStorage =
                SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
            return true;
        }
        else {
            refcntStorage = newRefcnt;
            return false;
        }
    }
    

    直接通过 sidetable_retain增加引用计数

    id
    objc_object::sidetable_retain()
    {
    #if SUPPORT_NONPOINTER_ISA // 是否支持优化过的isa指针
        ASSERT(!isa.nonpointer);
    #endif
        SideTable& table = SideTables()[this];
        
        table.lock();
        size_t& refcntStorage = table.refcnts[this];
        if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
            refcntStorage += SIDE_TABLE_RC_ONE; // // 引用计数加1,(偏移2位,SIDE_TABLE_RC_ONE = 1<<2)
        }
        table.unlock();
        return (id)this;
    }
    
    
    retain

    当我们在MRC手动调用 retain 方法时

    - (id)retain {
        return _objc_rootRetain(self);
    }
    id _objc_rootRetain(id obj)
    {
        ASSERT(obj);
    
        return obj->rootRetain();
    }
    
    ALWAYS_INLINE id  objc_object::rootRetain()
    {
        return rootRetain(false, false);
    }
    

    最后也调用到rootRetain

    获取对象的引用计数

    - (NSUInteger)retainCount {
        return _objc_rootRetainCount(self);
    }
    _objc_rootRetainCount(id obj)
    {
        ASSERT(obj);
    
        return obj->rootRetainCount();
    }
    

    rootRetainCount

    inline uintptr_t  objc_object::rootRetainCount()
    {
        //TaggedPointer指针 直接返回
        if (isTaggedPointer()) return (uintptr_t)this;
        sidetable_lock();
        isa_t bits = __c11_atomic_load((_Atomic uintptr_t *)&isa.bits, __ATOMIC_RELAXED);
        //优化过的 isa 指针
        if (bits.nonpointer) {
            uintptr_t rc = bits.extra_rc; // extra_rc存储的计数数量
            if (bits.has_sidetable_rc) { //bits.has_sidetable_rc标志位为1 表明有存放在sidetable中的引用计数
    //读取table的值 相加
                rc += sidetable_getExtraRC_nolock();
            }
            sidetable_unlock();
            return rc;
        }
    
        sidetable_unlock();
        //如果没采用优化的isa指针,则直接返回sidetable中的值
        return sidetable_retainCount();
    }
    

    retainCount = isa.extra_rc + sidetable_getExtraRC_nolock,即引用计数=isa指针中存储的引用计数+sidetable中存储的引用计数

    size_t  objc_object::sidetable_getExtraRC_nolock()
    {
        ASSERT(isa.nonpointer);
        //key是 this,存储了对象的table
        SideTable& table = SideTables()[this];
        //找到 it 否则返回0
        // 这里返回的it是RefcountMap类型 it == table.refcnts.end() 
        // 表示在sidetable中没有找到this对应的引用计数则直接返回0
        RefcountMap::iterator it = table.refcnts.find(this);
        if (it == table.refcnts.end()) return 0;
     // RefcountMap 结构的second值为引用计数值 
        // DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
            // it->second 无符号整型,真实的引用计数需要右移2位
        else return it->second >> SIDE_TABLE_RC_SHIFT;
    }
    

    objc_storeStrong

    // location是引用对象的指针的地址,obj是对象本身。
    void objc_storeStrong(id *location, id obj)
    {
    // 取到指针指的旧对象
        id prev = *location;
        if (obj == prev) {
            return;
        }
        objc_retain(obj); // 新对象计数+1
        *location = obj;
        objc_release(prev); // 旧对象计数-1
    }
    

    objc_release

    void  objc_release(id obj)
    {
        if (obj->isTaggedPointerOrNil()) return;
        return obj->release();
    }
    
    inline void
    objc_object::release()
    {
        ASSERT(!isTaggedPointer());
    
        rootRelease(true, RRVariant::FastOrMsgSend);
    }
    

    rootRelease

    ALWAYS_INLINE bool
    objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
    {
        if (slowpath(isTaggedPointer())) return false;
    
        bool sideTableLocked = false;
    
        isa_t newisa, oldisa;
    // 获取isa
        oldisa = LoadExclusive(&isa.bits);
    // 是否有自己的 release 方法
        if (variant == RRVariant::FastOrMsgSend) {
            // These checks are only meaningful for objc_release()
            // They are here so that we avoid a re-load of the isa.
            if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) {
                ClearExclusive(&isa.bits);
                if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {
                    swiftRelease.load(memory_order_relaxed)((id)this);
                    return true;
                }
                ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
                return true;
            }
        }
         // 是否是元类
        if (slowpath(!oldisa.nonpointer)) {
            // a Class is a Class forever, so we can perform this check once
            // outside of the CAS loop
            if (oldisa.getDecodedClass(false)->isMetaClass()) {
                ClearExclusive(&isa.bits);
                return false;
            }
        }
    
    retry:
        do {
    // 临时变量
            newisa = oldisa;
            if (slowpath(!newisa.nonpointer)) {
                ClearExclusive(&isa.bits);
    // 不是优化过的isa 直接release
                return sidetable_release(sideTableLocked, performDealloc);
            }
    // 正在释放,返回失败
            if (slowpath(newisa.isDeallocating())) {
                ClearExclusive(&isa.bits);
                if (sideTableLocked) {
                    ASSERT(variant == RRVariant::Full);
                    sidetable_unlock();
                }
                return false;
            }
    
            // 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--
            // 不够减,直接去 underflow 
            if (slowpath(carry)) {
                // don't ClearExclusive()
                goto underflow;
            }
        } while (slowpath(!StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits))); // 合并
    
        if (slowpath(newisa.isDeallocating())) // 是否需要释放
            goto deallocate; 
    
        if (variant == RRVariant::Full) {
            if (slowpath(sideTableLocked)) sidetable_unlock();
        } else {
            ASSERT(!sideTableLocked);
        }
        return false;
    
     underflow:
        // newisa.extra_rc-- underflowed: borrow from side table or deallocate
    
        // abandon newisa to undo the decrement
        newisa = oldisa;
    
        if (slowpath(newisa.has_sidetable_rc)) { // sidetable 是否存有引用计数
            if (variant != RRVariant::Full) {
                ClearExclusive(&isa.bits);
                return rootRelease_underflow(performDealloc); // 再次调用自己,只不过第二个参数是full
            }
    
            // Transfer retain count from side table to inline storage.
    
            if (!sideTableLocked) {
                ClearExclusive(&isa.bits);
                sidetable_lock();
                sideTableLocked = true;
                // Need to start over to avoid a race against 
                // the nonpointer -> raw pointer transition.
                oldisa = LoadExclusive(&isa.bits);
                goto retry; // 重新去减少引用计数
            }
    
            // 从sidetable分出来一部分引用计数
            // Try to remove some retain counts from the side table.        
            auto borrow = sidetable_subExtraRC_nolock(RC_HALF);
            // 是不是空的表
            bool emptySideTable = borrow.remaining == 0; // we'll clear the side table if no refcounts remain there
    
            if (borrow.borrowed > 0) {
                // Side table retain count decreased.
                // Try to add them to the inline count.
                bool didTransitionToDeallocating = false;
    // 增加 extra_rc 的值 ,但是要减去1.就不用再去减了
                newisa.extra_rc = borrow.borrowed - 1;  // redo the original decrement too
                newisa.has_sidetable_rc = !emptySideTable;
    
                bool stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits); // 合并
    
                if (!stored && oldisa.nonpointer) { // 合并失败,立刻重试
                    uintptr_t overflow;
                    newisa.bits =
                        addc(oldisa.bits, RC_ONE * (borrow.borrowed-1), 0, &overflow);
                    newisa.has_sidetable_rc = !emptySideTable;
                    if (!overflow) {
                        stored = StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits);
                        if (stored) {
                            didTransitionToDeallocating = newisa.isDeallocating(); // 是否需要释放
                        }
                    }
                }
    
                if (!stored) { // 还是合并失败,重新去走 retry 流程
                    ClearExclusive(&isa.bits);
                    sidetable_addExtraRC_nolock(borrow.borrowed);
                    oldisa = LoadExclusive(&isa.bits);
                    goto retry;
                }
    
                // Decrement successful after borrowing from side table.
                if (emptySideTable) // 空表,去释放表
                    sidetable_clearExtraRC_nolock();
    
                if (!didTransitionToDeallocating) { // 不需要释放就返回
                    if (slowpath(sideTableLocked)) sidetable_unlock();
                    return false;
                }
            }
            else {
                // Side table is empty after all. Fall-through to the dealloc path.
            }
        }
    
    deallocate:
        // 开始释放
    
        ASSERT(newisa.isDeallocating());
        ASSERT(isa.isDeallocating());
    
        if (slowpath(sideTableLocked)) sidetable_unlock();
    
        __c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
    
        if (performDealloc) { // 调用dealloc方法
            ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
        }
        return true;
    }
    

    sidetable_subExtraRC_nolock 从sidetable分出来一部分引用计数

    objc_object::SidetableBorrow objc_object::sidetable_subExtraRC_nolock(size_t delta_rc)
    {
        ASSERT(isa.nonpointer);
        SideTable& table = SideTables()[this];
    
        RefcountMap::iterator it = table.refcnts.find(this);
        if (it == table.refcnts.end()  ||  it->second == 0) {
            // Side table retain count is zero. Can't borrow.
    // Side table 没有引用计数
            return { 0, 0 };
        }
        size_t oldRefcnt = it->second;
    
        // isa-side bits should not be set here
        ASSERT((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
        ASSERT((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
      // 分出去引用计数 ,返回分出去的引用计数和剩余的引用计数
        size_t newRefcnt = oldRefcnt - (delta_rc << SIDE_TABLE_RC_SHIFT);
        ASSERT(oldRefcnt > newRefcnt);  // shouldn't underflow
        it->second = newRefcnt;
        return { delta_rc, newRefcnt >> SIDE_TABLE_RC_SHIFT };
    }
    

    release

    // Replaced by ObjectAlloc
    - (oneway void)release {
        _objc_rootRelease(self);
    }
    NEVER_INLINE void _objc_rootRelease(id obj)
    {
        ASSERT(obj);
    
        obj->rootRelease(); // 也调用到了上面的方法
    }
    

    也就是说,在引用计数减少的时候判断是否去释放

    hasCustomRR()

    // hasCustomRR函数通过掩码计算,检查类(包括其父类)中是否含有默认的方法,并且有则调用默认的方法;如果没有,调用其他方法.
    bool hasCustomRR() const {
            return !(bits.data()->flags & RW_HAS_DEFAULT_RR);
    }
    
     class_rw_t* data() const {
            return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    

    相关文章

      网友评论

          本文标题:Retain 和 Release

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