iOS - objc-weak
一、 知识补充
1. Tagged Pointer
一个对象在 32 bit CPU 下占 4 byte,在 64bit CPU下是占 8 byte
一个指针在 32 bit CPU下为 4 byte,在 64bit CPU下也是 8 byte
Tagged Pointer 专门用来存储小的对象
通常我们一个正常的对象需要通过指针寻址找到具体的值
类似 NSNumber、NSdata 4 个字节已经够用了,所以在 64 bit 中引入 Tagged Pointer
为了优化内存的使用,Tagged Pointer 即直接将值存储到指针中,8 byte 中 4 byte 存储值 4 byte 存储指针
2. bit-mask
在 objc-weak 中会看到诸如 index = w_hash_pointer(old_referrer) & (entry->mask)
的代码
这里使用的是位屏蔽 bit-mask
>> 例如通过 w_hash_pointer
散列计算得出 Value = 01010101
然后 Mask = 00001111
Mask = 00001111b
Value = 01010101b
Result = 00000101b
可以看出最后我们得到的结果总小于 Mask,意味着 Mask 用来作为 Value 最大值限制
那么 Mask 就应该是 Result 的最大值
二、数据结构
image.png- referrer - weak pointer - 弱引用对象地址
- referent - object pointer - 对象地址
二、方法解析
>> 1. hash 方法
Function | Description |
---|---|
hash_pointer |
散列对象指针 |
w_hash_pointer |
散列弱引用 |
>> weak reference
Function | Description |
---|---|
grow_refs_and_insert |
扩展表的大小,并且插入新的 weak reference |
append_referrer |
添加新的 weak reference 到 weak_entry |
remove_referrer |
在 weak_entry 中移除 weak reference |
>> weak_table 表大小
Function | Description |
---|---|
weak_resize |
重新设置 weak_ table 表的大小,并重新存储 weak_entry |
weak_grow_maybe |
如果 weak_table 中的 num_entries 超过表大小的 3/4 则 weak_resize 原来的两倍;若原先为 0 则默认为 64 |
weak_compact_maybe |
如果 weak_table 中的 num_entries 小于表大小的 1/16,或者表大小超过 1024 则 weak_resize 缩小为原来的 1/8 |
>> weak_table 中对 weak_entry 的操作
Function | Description |
---|---|
weak_entry_insert |
在 weak_table 中插入 weak_entry |
weak_entry_remove |
在 weak_table 中移除 weak_entry;并调用 weak_compact_maybe |
weak_entry_for_referent |
在 weak_table 中找到某个对象的 weak_entry |
>> weak_table 的 weak reference 操作
Function | Description |
---|---|
weak_unregister_no_lock |
在 weak_table 中移除某个对象的对应 weak reference |
weak_register_no_lock |
在 weak_table 中注册某个对象的对应 weak reference |
weak_clear_no_lock |
清除某个对象对应的所有 weak reference |
weak_read_no_lock |
获取某个 weak reference 对应的对象 |
三、结构体解析
1. SideTables
保存的是关于所有对象的表,这里的表分为两级
SideTables 是一个全局静态的表结构,表中保存的是 SideTable,key 对应的是对象的指针
// alignas(StripedMap<SideTable>) 内存对齐方式
alignas(StripedMap<SideTable>) static uint8_t
SideTableBuf[sizeof(StripedMap<SideTable>)];
static void SideTableInit() {
// 在 SideTableBuf 数组首地址中分配 StripedMap<SideTable>
new (SideTableBuf) StripedMap<SideTable>();
}
// StripedMap<SideTable>& 引用 SideTables() 返回的值
static StripedMap<SideTable>& SideTables() {
// 强转 SideTableBuf 为 StripedMap<SideTable>* 指针
return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
}
2. StripedMap
实际上也是散列表,一个大小为 8 的散列表
template<typename T>
class StripedMap {
enum { CacheLineSize = 64 };
enum { StripeCount = 8 };
struct PaddedT {
// 定义类型为 T 的变量 value,内存对齐 CacheLineSize
T value alignas(CacheLineSize);
};
// 定义一个个数为 StripeCount 的 Padded 元素数组
PaddedT array[StripeCount];
static unsigned int indexForPointer(const void *p) {
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
// stripedMap[p] 实质是一个表结构
// index 其实是指针,通过散列计算获取对应 T 的实例对象
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
const T& operator[] (const void *p) const {
return const_cast<StripedMap<T>>(this)[p];
}
};
3. SideTable
一个 SideTable 对应一个 weak_table
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
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(); }
bool trylock() { return slock.trylock(); }
// Address-ordered lock discipline for a pair of side tables.
template<bool HaveOld, bool HaveNew>
static void lockTwo(SideTable *lock1, SideTable *lock2);
template<bool HaveOld, bool HaveNew>
static void unlockTwo(SideTable *lock1, SideTable *lock2);
};
4. weak_table
存储 weak_entry 的结构体
struct weak_table {
// 存储 `weak_entry` 的数组,索引为 weak_entry->referent 的散列值
weak_entry_t *weak_entries;
// weak_entries 数组个数
size_t num_entries;
// weak_entries 的最大索引,用于 bit-mask;当 mask 改变时需要重新计算 weak_entry->referent 散列值
uintptr_t mask;
// 最大散列值
uintptr_t max_hash_displacement;
};
5. weak_entry
weak_entry 在 weak pointer 上存储分为两个部分:
- inline_referrers - 定长数组
- referrers - 动态数组,索引为 referrer 的散列值
当 referrer 个数不超过 WEAK_INLINE_COUNT
4 时,使用 inline_referrers,否则使用 referrers
#define WEAK_INLINE_COUNT 4
struct weak_entry_t {
// 源对象
DisguisedPtr<objc_object> referent;
union {
/* 动态数组 */
struct {
weak_referrer_t *referrers;
// 是否使用 referrers 的标志位,即是否超出 WEAK_INLINE_COUNT 个数
uintptr_t out_of_line : 1;
// referrers 中元素个数
uintptr_t num_refs : PTR_MINUS_1;
// referrers 的最大索引数量,用于 bit-mask;当 mask 改变时需要重新计算 referrer 散列值
uintptr_t mask;
// 最大散列值
uintptr_t max_hash_displacement;
};
/* 定长数组 */
struct {
// out_of_line=0 is LSB of one of these (don't care which)
weak_referrer_t inline_referrers[WEAK_INLINE_COUNT];
};
};
};
6. weak_referrer
对象指针的指针,即弱引用
typedef objc_object ** weak_referrer_t;