iOS weak 指针实现原理2
2019-02-20 本文已影响46人
大兵布莱恩特
SideTable 结构如下
struct SideTable {
spinlock_t slock; ///线程同步锁
RefcountMap refcnts; ///
weak_table_t weak_table; /// weak 散列表 所有 weak 指针存放在这个表里
};
weak_table_t 结构如下
struct weak_table_t {
weak_entry_t *weak_entries; ///指针数组 存放 weak_entry_t 类型
size_t num_entries; ///散列表最大可存放内容容量
uintptr_t mask; /// &mask 可以获取一个 key 从而在散列表快速查找某个元素
uintptr_t max_hash_displacement; ///hash key 最大偏移值
};
objc_object::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) { /// 有弱引用指向 就将这个对象的指针置nil
weak_clear_no_lock(&table.weak_table, (id)this);
}
///对 isa 指针的引用技术管理
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock(); ///解锁
}
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
将 weak 散列表地址和当前对象实例传递进来
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
///当前对象强转为 objc_object 结构体指针类型
objc_object *referent = (objc_object *)referent_id;
///获weak散列表中跟当前对象对应的 weak_entry_t *结构体指针
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) { ///如果为 nil 代表 没有查找到这个对象在 weak 表里存放 可能是这个对象转成CF对象 并转移了对象引用计数所有权
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// 存放所有 weak 指针数组
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
///weak 指针数组中的元素 是 当前对象指向的指针地址 将指针置空 nil
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
///从 weak 表中移除跟当前对象weak_entry_t有关的内容
weak_entry_remove(weak_table, entry);
}
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent) 根据当前对象指针作为查找条件从 weak_table_t 中获取 weak_entry_t * 指针类型结构体 这里边是一个简单的 hash 算法 用对象内存地址转成一个 hash 值 然后 & weak_table->mask 获得一个开始索引 然后根据这个索引在数组里查找元素 ,如果生成的索引查找的元素不是我们想要的 ,可以用 index + 1 & weak_table->mask 重新生成一个 index , 直到算出一个合适的索引 ,然后从数组里 取出 一个 weak_entry_t * 结构体指针
static weak_entry_t *
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent)
{
assert(referent);
weak_entry_t *weak_entries = weak_table->weak_entries;
if (!weak_entries) return nil;
///用对象内存地址转成一个 hash 值 然后 & weak_table->mask 获得一个开始索引
size_t begin = hash_pointer(referent) & weak_table->mask;
size_t index = begin;
size_t hash_displacement = 0;
///直到 index 对应的 weak_entries[index].referent == referent 是我们要释放掉的对象时 才会退出循环
while (weak_table->weak_entries[index].referent != referent) {
/// 用 index + 1 方式 生成一个新的 index 继续在 weak_entries查找
index = (index+1) & weak_table->mask;
if (index == begin) bad_weak_table(weak_table->weak_entries);
hash_displacement++;
if (hash_displacement > weak_table->max_hash_displacement) {
return nil;
}
}
///根据计算出来的 index 取出一个weak_entry_t * 结构体指针
return &weak_table->weak_entries[index];
}
好了,我是大兵布莱恩特,欢迎加入博主技术交流群,iOS 开发交流群
QQ20180712-0.png