retain的实现原理

1. retain的实现原理

ALWAYS_INLINE id 
objc_object::rootRetain(bool tryRetain, bool handleOverflow)
{
    // TaggedPointer 不retain直接返回
    if (isTaggedPointer()) return (id)this;

    bool sideTableLocked = false;
    bool transcribeToSideTable = false;

    isa_t oldisa;
    isa_t newisa;

    do {
        // 默认不抄写到SideTable,isa指针5w+足以
        transcribeToSideTable = false;
        // 原子获取isa
        oldisa = LoadExclusive(&isa.bits);
        newisa = oldisa;
        
        // 非共用体isa, 一定会有一个结果,不会do-while的
        if (slowpath(!newisa.nonpointer)) {
            // 原子清空对象的isa.bits
            ClearExclusive(&isa.bits);
            // isa 指向原类,说明是类对象,直接返回
            if (rawISA()->isMetaClass())
                return (id)this;
            // retain 必须成功返回true,那么如果当前线程锁住了sideTable对象,需要解锁。
            if (!tryRetain && sideTableLocked)
                sidetable_unlock();
            if (tryRetain)
                // try retain 会返回bool值以判定是否retain成功,由调用者自行do-while重试。
                return sidetable_tryRetain() ? (id)this : nil;
            else
                // retain 必须成功返回true,所以sidetable_retain会加lock。
                return sidetable_retain();
        }
        
        // don't check newisa.fast_rr; we already called any RR overrides
        if (slowpath(tryRetain && newisa.deallocating)) {
            // 正在释放,原子清空isa.bits
            ClearExclusive(&isa.bits);
            if (!tryRetain && sideTableLocked)
                // 释放sidetable资源
                sidetable_unlock();
            return nil;
        }
        
        // 把new isa改一改,CAS操作失败继续do-while,直至成功
        uintptr_t carry;
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        // isa extra_rc值超过2^19
        if (slowpath(carry)) {
            // 不处理越界,什么鬼?
            if (!handleOverflow) {
                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加锁。如果等isa改完后再锁sidetable就完了。在isa和sidetable需要同时调整时,两件事是一个完成的事务。
                sidetable_lock();
            // 标记下extra_rc满了extra_rc设置一半
            sideTableLocked = true;
            transcribeToSideTable = true;
            newisa.extra_rc = RC_HALF;
            newisa.has_sidetable_rc = true;
        }
    } while (slowpath(!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)));

    
    if (slowpath(transcribeToSideTable)) {
        // 拷贝另一半到side table. 那么为什么一人一半呢 ?
        // 因为,isa操作快啊
        // 那么为什么不全拷贝过去?
        // 因为还要release,ISA减到0又要拷贝部分过来不是吗,所以是兄弟就AA吧。
        sidetable_addExtraRC_nolock(RC_HALF);
    }
    // 所有操作完成,解锁
    if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
    return (id)this;
}
-------------
id
objc_object::sidetable_retain()
{
#if SUPPORT_NONPOINTER_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;
    }
    table.unlock();

    return (id)this;
}
  • 注意点:
  1. 修改ISA通过自旋CAS完成,在这种场景下,冲突的可能性小,性能高。
  2. 修改SideTables时通过加锁完成,所以性能会差很多(weak引用同理)
  3. 为了权衡isa 2^19的空间限制问题和性能问题,Apple采用了HALF_RC平均分配(还有release,需要考虑isa extra_rc减到0需要拷贝回来的问题)。
  4. extra_rc 与 sideTable需要同时,需要注意事务的原子性和隔离性,提前加锁。

release和retain的原理和注意点相似,倒过来就好了。

你可能感兴趣的:(retain的实现原理)