内存管理篇:8.ARC实现之__weak
我们知道,weak修饰的变量,其地址是被存储在一个专用的散列表中,此散列表的键值为原内存的散列值(即对象地址的hash值)。
__weak修饰的变量,其主要存在两大功能:
- 当其引用的对象被释放时,此变量自动被赋值为nil;
- 使用__weak修饰的变量时,实际上使用的是添加到autoreleasePool中的对象。
当__weak变量引用的对象被释放时,其自动被赋值为nil
先看示例:
{
// obj为强引用变量且已经被赋值
id __weak obj1 = obj;
}
编译后的模拟代码为:
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(&obj1);
objc_initWeak方法,即为:
obj1 = 0;
objc_storeWeak(&obj1, obj);
objc_destroyWeak方法,即为:
objc_storeWeak(&obj1, 0);
由此可以看出,
- 对于初始化的weak变量:为变量赋值为0,并将其地址存储到weak表中,对象内存的散列值所在的地址中。
- 释放weak变量时:则是在weak表中,将对象内存散列值所在地址存储的数据中,将存储的变量清空。
由于可以使用多个weak变量指向同一对象,所以在weak表中,一个键值的散列地址中可以存储多个weak变量地址。
结论:
weak对象释放时,自动置为nil的原因:
- 在系统调用dealloc释放对象时(最后的objc_clear_deallocating方法),ARC会根据对象的引用状态,去weak表中查询对应的weak变量,将变量地址赋值为nil,并将其记录删除。最后将此键值记录一并删除。
- 注意:此过程需要消耗CPU资源,故不要滥用,需要时再使用(如避免delegate和block导致的引用循环)。
使用__weak修饰的变量时,实际上使用的是添加到autoreleasePool中的对象
还是先看实例:
{
// obj为强引用变量,且已被赋值
id __weak obj1 = obj;
NSLog(@"%@", obj1);
}
编译后的模拟代码为:
// 初始化weak变量并赋值
id obj1;
objc_initWeak(&obj1, obj);
// ???
id tmp = objc_loadWeakRetained(&obj1);
// ???
objc_autorelease(tmp);
NSLog(@"%@", tmp);
// 释放weak变量
objc_destroyWeak(&obj1);
其中:
- 先通过objc_loadWeakRetained方法,将weak变量对应引用的对象取出,并进行retain操作。
- 使用objc_autorelease方法,将生成的临时对象加入到autoreleasePool中,即可保证对象的生存期,以便正常使用。
注意:
- 大量使用weak变量,会在autoreleasePool中插入大量临时变量,增加内存开销,并对CPU进行过多无畏的损耗。
- 正确的使用方法,是使用strong变量指向weak变量后,再进行使用。
id __weak obj1 = obj;
id tmp = obj1;
NSLog(@"1.%@", tmp);
NSLog(@"2.%@", tmp);
NSLog(@"3.%@", tmp);
NSLog(@"4.%@", tmp);
NSLog(@"5.%@", tmp);
// 这样,autoreleasePool只会将obj1插入一次