iOS weak关键字实现原理

2023-11-20

在iOS中,使用weak关键字能够对内存对象进行弱引用,基于这个特性,使用weak关键字能够解决许多问题,例如delegate中对象的循环持有问题、Block对对象的强引用导致的对象无法及时释放问题。

为何weak关键字能够实现对内存对象的弱引用,今天我们就来探究一下。

首先在分析weak关键字实现原理之前,先介绍一下相关的数据结构,这些数据结构其中一部分可能在其他地方有所提及,但本文只列出与weak关键字有关的一部分。

这些数据结构全部存在于runtime源码中,相关内容可以在 objc-weak文件中查看。

一、数据结构

1. SideTables

SideTables本质上是一个全局的 StripedMap

StripedMap本质是一个数组,且在iOS系统下,容量为64。

该数据结构通过实现[]操作,实现了类似字典的功能:可通过传入一个对象作为key值,来获取对应的Item。

SideTables中, Item类型为 SideTable,由此可见,对于任何一个对象, SideTables都能根据其地址对应到具体的一个 SideTable上。

2. SideTable

SideTable中包含三个元素,分别是 1.自旋锁 2.记录对象引用计数的字典 3.记录对象弱引用信息的数据结构 weak_table_t

其中 weak_table_t是与weak关键字有关的数据结构,其余二者暂可不用关注。

3. weak_table_t

weak_table_t本质上是一个数组,其中每个Item为 weak_entry_t

4. weak_entry_t

weak_entry_t就比较有意思了,它本质上是个字典。

其中的key值为对象,而value对应为一个数组,该数组最初为内部的一个大小为4的数组,当数组大小超过4后,则变为内部一个可变大小数组。

无论value值对应的数组是固定大小还是可变大小,数组中保存的值均为 weak_referrer_t类型的数据。

5. weak_referrer_t

weak_referrer_t本质上是 objc_object **,即Objective-C对象的地址。

所以,weak_entry_tvalue数组中,每一个Item均为一个地址,即weak对象的地址。

以上就是weak实现原理中所涉及到的所有数据结构,具体关系如下图:

Weak数据结构关系图

二、weak_table_tweak_entry_t相关方法

在正式探究weak关键字实现原理之前,先来看一些操作 weak_table_tweak_entry_t的方法。

1. 从 weak_table_t中查询对应的 weak_entry_t

static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
    //获取weak_table_t的数组结构
    weak_entry_t *weak_entries = weak_table->weak_entries;

    if (!weak_entries) return nil;

    //获取对象地址,并根据地址映射到数组结构长度内,得到对应下标
    size_t index = hash_pointer(referent) & weak_table->mask;
    //线性探寻数组结构中对应的value所在index
    size_t hash_displacement = 0;
    while (weak_table->weak_entries[index].referent != referent) {
        index = (index+1) & weak_table->mask;
        hash_displacement++;
        if (hash_displacement > weak_table->max_hash_displacement) {
            return nil;
        }
    }
    
    //返回查询到的weak_entry_t
    return &weak_table->weak_entries[index];
}

2. 向 weak_table_t中增加新的 weak_entry_t

static void 
weak_entry_insert(weak_table_t *weak_table, weak_entry_t *new_entry)
{
    //获取weak_table_t的数组结构
    weak_entry_t *weak_entries = weak_table->weak_entries;
    assert(weak_entries != nil);

    //获取对象地址,并根据地址映射到数组结构长度内,得到对应下标
    size_t index = hash_pointer(new_entry->referent) & (weak_table->mask);
    //线性探寻数组结构中value所应在的位置
    size_t hash_displacement = 0;
    while (weak_entries[index].referent != nil) {
        index = (index+1) & weak_table->mask;
        hash_displacement++;
    }

    //将```weak_entry_t```放入```weak_table_t```对应位置,并更新相关数据
    weak_entries[index] = *new_entry;
    weak_table->num_entries++;

    if (hash_displacement > weak_table->max_hash_displacement) {
        weak_table->max_hash_displacement = hash_displacement;
    }
}

3. 扩展 weak_table_t容积

weak_entry_insert方法不需要考虑 weak_table_t容积,因为runtime代码中在调用 weak_entry_insert方法前都会调用 weak_grow_maybe方法来在必要的时候扩展 weak_table_t容积。

static void 
weak_grow_maybe(weak_table_t *weak_table)
{
    size_t old_size = TABLE_SIZE(w
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

iOS weak关键字实现原理 的相关文章

随机推荐