聊一聊weak
2020-12-11 本文已影响0人
晨阳Xia
1.聊一聊附有__weak修饰符的变量背添加到弱引用表,以及在弱引用表中删除的过程
附有__weak修饰符的变量,指向一个alloc生成的对象,编译之后的代码

编译前:
{
id __weak obj1 = obj;
}
编译后:
id obj1;
/*
location:附有__weak修饰符的变量
newObj:被引用的对象
*/
1.
objc_initWeak(id *location, id newObj) {
if (!newObj) {
*location = nil
return nil
}
return storeWeak<false/*old*/, true/*new*/, true/*crash*/>(location, (objc_object*)newObj)
}
2.
storeWeak(id *location, id newObj) {
...
SideTable *oldTable;
SideTable *newTable;
...
newTable = &SideTables()[newObj] // 以对象为键值,在散列表中中找到newTable
...
newObj = (objc_bjectt)weak_register_no_lock(&newTable->weak_table,(id)newObj,location,CrashIfDellocating)
...
// 赋值
*location = (id)newObj
}
3.
/*
referent_id:被引用的对象
*referrer_id:附有__weak修饰符的变量
*/
weak_register_no_lock(weak_table *weak_table, id referent_id, id *referrer_id, bool crashIfDellocating) {
objc_object *referent = (objc_object *)referent_id;
objc_object *referrer = (objc_object **)referrer_id;
...
weak_entry *entry; // 弱引用数组(理解为数组,其实比数组更复杂)
if (entry = weak_entry_for_referent(weak_table, referent)) {
append_referrer(entry, referrer);
} else { // 没有则创建
weak_entry_t *new_entry;
new_entry.referent = referent;
new_entry.out_of_line = ;
new_entry.inline_referrer[0] = referrer;
for (i = 1; i < WEAK_INLINE_COUNT; i++) {
new_entry.inline_referrer[i] = nil;
}
}
weak_grow_maybe(weak_table);
// 将数组插入散列表中
weak_insert_entry(weak_table, &new_entry);
}
4.
weak_entry_for_referent(weak_table_t *weak_table, objc_object *referent) {
weak_entry_t weak_entries = weak_table->weak_entries;
if (!weak_entries) {
retrun nil;
}
...
size_t index = hash_pointer(referent) & weak_table->mash;
// 哈希冲突
// 当前索引拿到的对象不是传入referent,则索引加1,往下查找。
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;
}
}
...
return &weak_table->entries[index]
}
2.objc_initWeak 内部调用 objc_storeWeak函数
obj1 = 0
objc_storeWeak(&obj1, obj)
变量作用域结束时,调用以下函数销毁:
objc_destroyWeak(&obj1, 0);
2.objc_destroyWeak 内部调用 objc_storeWeak函数
objc_storeWeak(&obj1, 0)
2.若附有__weak修饰符的变量所引用的对象被废弃,则将该变量赋值为nil
weak_clear_no_lock(weak_table_t *weak_table, id referent_id){
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_refetent(weak_table, referent);
if (!entry) {
retrun nil;
}
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;
}
// 将引用对象的所有变量都置为nil
for (size_t = 0; i < count; i++) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
}
}
}
3.使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象
1.使用__weak 修饰符时,一下代码会引起警告:
{
id __weak obj = [[NSObject alloc] init];
NSLog(@"obj = %@", obj) // 结果: obj == (null)
}
/* 编译器的模拟代码 */
id obj1;
id tmp = objc_msgSend(NSObject,@selector(alloc));
objc_msgSend(tmp, @seletor(init));
objc_initWeak(&obj1, tmp);
objc_release(tmp)
objc_destoryWeak(&obj1)
虽然自己生成并持有的对象通过objc_initWeak函数被赋值给__weak修饰符的变量中,但编译器判断其没有持有者,故该对象立即通过objc_release函数被释放和废弃。
2.使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象
{
id __weak obj1 = obj;
}
/* 编译器的模拟代码 */
id obj1;
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1)
objc_autorelease(tmp)
objc_destoryWeak(&obj1)
(1)objc_loadWeakRetained函数取出附有__weak修饰符变量所引用的对象并retain
(2) objc_autorelease函数将对象注册到autoreleasepool中
由此可知,因为附有__weak修饰符变量所引用的对象想这样被注册到aotureleasepool中 ,所以在@autoreleasepool块结束之前都可以放心的使用。但是如果大量的使用附有__weak修饰符的变量,注册到autoreleasasepool的对象也会大量的增加.因此在使用附有__weak修饰符的变量时,最好先暂时赋值给附有__strong修饰符的变量后再使用。
图片举例


独自实现引用计数机制的类大多不支持__weak修饰符。
4.注意
大量使用附有__weak修饰符的变量,会消耗相应的cpu资源
参考文献
- 《编写高质量iOS与OS X代码的52个有效方法》
- 《Objective-C高级编程》