# 02.Retain\&Release

### 前言

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

经过[上文](/wiki/ios/nei-cun-guan-li/01.-cong-yi-ge-mian-shi-ti-kan-taggedpointer.md)对 `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](/wiki/ios/di-ceng/03.-dui-xiang-ben-zhi-tan-jiu-yu-isa.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ryukiedev.gitbook.io/wiki/ios/nei-cun-guan-li/02.retain-and-release.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
