# 02.Retain\&Release

### 前言

到现在我们接触到了三种指针： `TaggedPointer` 、 `Nonpointer isa` 和 `纯isa` 。

经过[上文](https://ryukiedev.gitbook.io/wiki/ios/nei-cun-guan-li/01.-cong-yi-ge-mian-shi-ti-kan-taggedpointer)对 `TaggedPointer` 的探索，我们接触到了 `Reatin` 和 `Release` ，发现他们对 `TaggedPointer` 有特殊处理的。本文将更加深入的对 `Reatin` 和 `Release` 进行研究。

### 一、 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`
  * `BITFIELD` 和 `SideTable` 结合使用
    * 当 `BITFIELD` 的 `extra_rc` 容量足够的时候持续 ++
    * 当 `BITFIELD` 的 `extra_rc` 容量不够的时候
      * `extra_rc` 减半，拷贝一半到 `SideTable` 进行管理
      * 同时标志位 `has_sidetable_rc` 改变
* `纯isa`
  * 通过 `SideTable` 管理

### 二、 Release

```
objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
{
    // 不处理 TaggedPointer 直接 return
    if (slowpath(isTaggedPointer())) return false;

    ...

retry:
    do {
        newisa = oldisa;
        if (slowpath(!newisa.nonpointer)) {
            // 非 nonpointer 通过 sidetable 处理
            ClearExclusive(&isa.bits);
            return sidetable_release(sideTableLocked, 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()
            // extra_rc 见底 跳转执行 underflow
            goto underflow;
        }
    } while (slowpath(!StoreReleaseExclusive(&isa.bits, &oldisa.bits, newisa.bits)));

    if (slowpath(newisa.isDeallocating()))
        goto deallocate;

    ...
    return false;

 underflow:
    // extra_rc 见底后，从 SideTable 拿回引用计数，或者销毁
    // 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);
        }

        // 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 及标志位
            newisa.extra_rc = borrow.borrowed - 1;  // redo the original decrement too
            newisa.has_sidetable_rc = !emptySideTable;
            ...
        }
        else {
            // Side table is empty after all. Fall-through to the dealloc path.
        }
    }

deallocate:
    // Really deallocate.
    // 销毁
    ...

    if (performDealloc) {
        ((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
    }
    return true;
}
```

#### 小结

* `TaggedPointer` 不处理，直接 return
* `Nonpointer isa`
  * `BITFIELD` 和 `SideTable` 结合使用
    * 当 `BITFIELD` 的 `extra_rc` 容量未见底的时候持续 --
    * 当 `BITFIELD` 的 `extra_rc` 容量见底的时候
      * 检查标志位 `has_sidetable_rc`
        * 使用了 `SideTable`
          * 将 `SideTable` 中的引用计数转移回 `BITFIELD` 的 `extra_rc` 进行处理
        * 未使用 `SideTable`
          * 需要销毁
* `纯isa`
  * 通过 `SideTable` 管理

### 三、 总结

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

### 参考

[对象本质探究与isa](https://ryukiedev.gitbook.io/wiki/ios/di-ceng/03.-dui-xiang-ben-zhi-tan-jiu-yu-isa)
