# 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](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-ab2ae4fff726a84b5e55ef8624d7750be25ca4bf%2F03-01.png?alt=media)

走到了 objc\_initWeak

![2](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-d587c58e5e4ecf4d515ba7fc67f1a56364a1cae3%2F03-02.png?alt=media)

**objc\_initWeak**

![3](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-96d0579c6cbd43d895da2c2a028b255eb9389ded%2F03-03.png?alt=media)

**storeWeak**

![4](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-9865a39f13b298d939914e8aefc3afd703343ebd%2F03-04.png?alt=media)

**weak\_register\_no\_lock**

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

参数：

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

![5](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-5fbfd25160d126bb25a5e070b5b89580961d1f23%2F03-05.png?alt=media)

**append\_referrer**

把 referrer 加进 entry 的 inline\_referrers

![6](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-59066f0aa167ca2b8a368defe0a593e495b6380c%2F03-06.png?alt=media)

> 这里又见到了熟悉的 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](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-407ccde5a99e6ad44ed4b23842f8b595b8dec3d3%2F03-07.png?alt=media)

来到了 `objc_loadWeakRetained`

![8](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-d1e3988ccb0799e8cf2c436a3f92c6e8432a6f25%2F03-08.png?alt=media)

**objc\_loadWeakRetained**

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

![9](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MI8JgbGh3U6X_oedqkm%2Fuploads%2Fgit-blob-b0e88ccf1a25c60ae59d82d41c17d150ccbd7002%2F03-09.png?alt=media)

### 总结

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