02.Retain&Release

前言

到现在我们接触到了三种指针: TaggedPointerNonpointer isa纯isa

经过上文TaggedPointer 的探索,我们接触到了 ReatinRelease ,发现他们对 TaggedPointer 有特殊处理的。本文将更加深入的对 ReatinRelease 进行研究。

一、 Retain

objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
{
    // 不处理 TaggedPointer 直接 return
    if (slowpath(isTaggedPointer())) return (id)this;

    ...

    do {
        transcribeToSideTable = false;
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) {
            // 非 nonpointer 通过 sidetable 处理
            ClearExclusive(&isa.bits);
            if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;
            else return sidetable_retain(sideTableLocked);
        }
        
        ...

        uintptr_t carry; // 标志 extra_rc 容量是否还能够承受
        newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++

        if (slowpath(carry)) {
            // 超出 extra_rc 容量
            // newisa.extra_rc++ overflowed
            if (variant != RRVariant::Full) {
                ClearExclusive(&isa.bits);
                return rootRetain_overflow(tryRetain);
            }
            // 保留一般的引用计数,并将另一半拷贝到 side table
            // 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;
        }
    } while (slowpath(!StoreExclusive(&isa.bits, &oldisa.bits, newisa.bits)));

    if (variant == RRVariant::Full) {
        if (slowpath(transcribeToSideTable)) {
            // Copy the other half of the retain counts to the side table.
            sidetable_addExtraRC_nolock(RC_HALF);
        }

        if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock();
    } else {
        ASSERT(!transcribeToSideTable);
        ASSERT(!sideTableLocked);
    }

    return (id)this;
}

小结

  • TaggedPointer 不处理,直接 return

  • Nonpointer isa

    • BITFIELDSideTable 结合使用

      • BITFIELDextra_rc 容量足够的时候持续 ++

      • BITFIELDextra_rc 容量不够的时候

        • extra_rc 减半,拷贝一半到 SideTable 进行管理

        • 同时标志位 has_sidetable_rc 改变

  • 纯isa

    • 通过 SideTable 管理

二、 Release

小结

  • TaggedPointer 不处理,直接 return

  • Nonpointer isa

    • BITFIELDSideTable 结合使用

      • BITFIELDextra_rc 容量未见底的时候持续 --

      • BITFIELDextra_rc 容量见底的时候

        • 检查标志位 has_sidetable_rc

          • 使用了 SideTable

            • SideTable 中的引用计数转移回 BITFIELDextra_rc 进行处理

          • 未使用 SideTable

            • 需要销毁

  • 纯isa

    • 通过 SideTable 管理

三、 总结

通过对 Retain 和 Release 源码的探索,发现使用 TaggedPointerNonpointer isa 在内存管理的效率上会更高。一旦 SideTable 介入,流程就回变的复杂还要上锁解锁,影响效率。虽然一般开发过程中很难遇到这种情况,但是理解了这些后,对一些问题一些现象就能更清楚的看到本质。

参考

对象本质探究与isa

Last updated

Was this helpful?