# 03.SideTable和weak底层实现

我们先看一个小题目，看看下面的输出回事什么样的？

```
NSObject *objc = [[NSObject alloc] init];

NSLog(@"%zd - %@",(long)CFGetRetainCount((__bridge CFTypeRef)(objc)), objc);

__weak typeof(id) weakObjc = objc;

NSLog(@"%zd - %@",(long)CFGetRetainCount((__bridge CFTypeRef)(objc)), objc);

NSLog(@"%zd - %@",(long)CFGetRetainCount((__bridge CFTypeRef)(weakObjc)), weakObjc);
```

输出：

> 1、1、2。是不是很迷惑？别急我们慢慢来

### 前言

前面在对内存管理进行研究的时候接触到了 `SideTalbe` ，今天我们也依然会和他见面，它究竟是什么呢？

```
void 
objc_object::sidetable_lock()
{
    SideTable& table = SideTables()[this];
    table.lock();
}
```

`SideTable` 可以看出， `SideTable` 是有多张的。它是一个 `HashMap` 。

### 一、 SideTable 的结构

```
struct SideTable {
    spinlock_t slock;
    // 引用计数 Map
    RefcountMap refcnts;
    // 若引用 Map
    weak_table_t weak_table;

    SideTable() {
        memset(&weak_table, 0, sizeof(weak_table));
    }

    ~SideTable() {
        _objc_fatal("Do not delete SideTable.");
    }

    void lock() { slock.lock(); }
    void unlock() { slock.unlock(); }
    void forceReset() { slock.forceReset(); }

    ...
};
```

简单了解了 SideTable 后我们继续回到题目。

### 二、 weak 做了什么

#### 2.1 调试

在这里打上汇编断点

![1](/files/mEOLDCrNTQWDWkrDEdmJ)

走到了 objc\_initWeak

![2](/files/GC6obO7D7TbyC9b8ghMg)

**objc\_initWeak**

![3](/files/HTXAP3Qp03UxhyaMOEDv)

**storeWeak**

![4](/files/OzirUOj6a8wHGLjOTyhv)

**weak\_register\_no\_lock**

注册一个 对象 - weak指针 对。如果没有的话，创建一个 weak 的 entry 对象。

参数：

* weak\_table : SideTable 地址
* referent : 被弱引用的对象
* referrer : 弱引用指针地址

![5](/files/xs8tx05qztixNMv2e9te)

**append\_referrer**

把 referrer 加进 entry 的 inline\_referrers

![6](/files/DqMKuy1Ah5O2BaRySrjn)

> 这里又见到了熟悉的 3/4 扩容

**weak\_entry\_t**

```
struct weak_entry_t {
    DisguisedPtr<objc_object> referent;
    union {
        struct {
            // 一个对象可以有多个 referent
            weak_referrer_t *referrers;
            uintptr_t        out_of_line_ness : 2;
            uintptr_t        num_refs : PTR_MINUS_2;
            uintptr_t        mask;
            uintptr_t        max_hash_displacement;
        };
        struct {
            // out_of_line_ness field is low bits of inline_referrers[1]
            weak_referrer_t  inline_referrers[WEAK_INLINE_COUNT];
        };
    };

    ...

    weak_entry_t(objc_object *newReferent, objc_object **newReferrer)
        : referent(newReferent)
    {
        inline_referrers[0] = newReferrer;
        for (int i = 1; i < WEAK_INLINE_COUNT; i++) {
            inline_referrers[i] = nil;
        }
    }
};
```

### 三、 回到题目继续分析

我们在这里下个断点，并打开汇编断点：

![7](/files/HwwjBy1mIV92XCNXcFwI)

来到了 `objc_loadWeakRetained`

![8](/files/IPPRwWg7srPY8C1ogAUQ)

**objc\_loadWeakRetained**

内部继续跟进来到了 `rootTryRetain` ，这里内部引用计数会加

![9](/files/BronjabHvujVxmax6Pjy)

### 总结

* `weak` 原理
  * 得到 `SideTable` 的弱引用表
  * 得到一个 `weak_entry_t`
  * 把 `referent` 加入到 `entry` 的数组 `inline_referrers`
  * 如果 `weak_table` 需要扩容
    * 3/4 扩容，类似与方法列表
  * 把 `new_entry` 加入到 `weak_table` 中
* 当我们使用一个 `weakObjc` 的时候，会调用 `rootTryRetain` ，所以引用计数会 +1。也就是这里我们看到输出为 2 的原因所在。


---

# 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/03.sidetable-he-weak-di-ceng-shi-xian.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.
