引用计数
为了管理所有对象的引用计数和weak指针,苹果创建了一个全局的SideTables,虽然名字后面有个"s"不过他其实是一个全局的Hash表,里面的内容装的都是SideTable结构体而已。它使用对象的内存地址当它的key
isa指针
Snip20200712_10.png
引用计数可以直接存储在优化过的isa指针中,也可能存储在SideTable中
struct SideTable {
spinlock_t slock;
RefcountMap refcnts;
weak_table_t weak_table;
};
refcnts是一个存放着对象引用计数的散列表
获取引用计数
objc_object::rootRetainCount()
{
if (isTaggedPointer()) return (uintptr_t)this;
sidetable_lock();
isa_t bits = LoadExclusive(&isa.bits);
ClearExclusive(&isa.bits);
if (bits.nonpointer) { // 优化过的isa
uintptr_t rc = 1 + bits.extra_rc;
if (bits.has_sidetable_rc) { // 存储在Sidetable中
rc += sidetable_getExtraRC_nolock();
}
sidetable_unlock();
return rc;
}
sidetable_unlock();
return sidetable_retainCount();
}
objc_object::sidetable_getExtraRC_nolock()
{
assert(isa.nonpointer);
//SideTables是全局的Hash表
SideTable& table = SideTables()[this]; // 对象self本身是key
RefcountMap::iterator it = table.refcnts.find(this); // 对象self本身是key
if (it == table.refcnts.end()) return 0;
else return it->second >> SIDE_TABLE_RC_SHIFT;
}
dealloc调用流程
Snip20200712_11.pngweak指针销毁过程
dealloc 调用
clearDeallocating函数里面又调用clearDeallocating_slow
objc_object::clearDeallocating_slow()
{
assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this); // 遍历删除对应对象的弱指针
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
//SideTable中的weak_table_t 从下面的注释可以看出是全局的
/**
* The global weak references table. Stores object ids as keys,
* and weak_entry_t structs as their values.
*/
struct weak_table_t {
weak_entry_t *weak_entries;
size_t num_entries;
uintptr_t mask;
uintptr_t max_hash_displacement;
};
//其中weak_entry_t
truct weak_entry_t {
DisguisedPtr<objc_object> referent;
}
总结
weak是Runtime维护了一个hash(哈希)表(weak_table_t),用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象指针的地址)数组。
1、初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
2、添加引用时:objc_initWeak函数会调用 storeWeak() 函数, storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
3、释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。
网友评论