做过iOS开发的都知道 strong、autoreleasing修饰符,对weak可能就更熟悉了。
笔者最近看书,才发现对__weak修饰符的理解并不是很准确。
我之前的理解:
id __weak obj = [[NSObject alloc] init];
/* 编译器的模拟代码 */
id obj;
id tmp = obj_msgSend(NSObject, @selector(alloc));
objc_msgSend(tmp, @selector(init));
objc_initWeak(&obj, tmp);
objc_release(tmp);
objc_destoryWeak(&obj);
weak修饰的参数在赋值的时候引用计数是不加一的,所以在 obj = anyObject 的情况下,如果anyObject的引用计数不等于0的情况下,obj 是指向 &anyObject。
当anyObject的引用计数为0的时候,这个时候obj就会自动的 obj = nil。
obj并不能持有对象,所以在指向的这个对象被释放的时候,编译器执行 obj = nil。
我在这里的理解有一些偏差,我认为obj是永远都不会注册到autoreleasepool的。(事实上并不是永远)
书本上的原话:
若附有 __weak 修饰符的的变量所引用的对象被废弃,则将 nil 赋值给该变量。
使用附有 __weak 修饰符的变量,即是使用注册到 autoreleasepool 中的对象。
实际的情况:
weak修饰的变量 在使用的时候 有注册到autoreleasepool。
id __weak obj1 = obj; // 这里的obj是 __strong 修饰的变量
NSLog(@"%@", obj1);
/* 编译器的模拟代码 */
id obj1;
objc_initWeak(&obj1, obj);
// ----在这里回车,读者会更好理解----
id tmp = objc_loadWeakRetained(&obj);
objc_autorelease(tmp);
NSLog(@"%@", tmp);
objc_destoryWeak(&obj1);
我们可以看到
- objc_loadWeakRetained 函数取出附有 __weak 修饰符变量所引用的对象并 retain。
- objc_autorelease 函数将对象注册到 autoreleasepool 中。
总结:
id __weak obj = [[NSObject alloc] init]; // obj这个参数一被赋值马上被释放
// 在 obj 释放之前,obj1 != nil。
id __weak obj1 = obj;
// 可以理解为,编译器使用weak修饰的变量时,需要先转为strong修饰再调用。
// 每次使用到obj1的时候都会生成一个 id tmp 指向 &obj。(id tmp 默认为__strong)
// 所以这里生成的 id tmp 会导致 &obj 的引用计数+1
NSLog(@"%@", obj1);
id __weak obj1 = obj;
NSLog(@"1 %@", obj1);
NSLog(@"2 %@", obj1);
NSLog(@"3 %@", obj1);
NSLog(@"4 %@", obj1);
NSLog(@"5 %@", obj1);
// 变量 obj1 所赋值的对象注册到 autoreleasepool 5次。
id __weak obj1 = obj;
id tmp = obj1;
NSLog(@"1 %@", tmp);
NSLog(@"2 %@", tmp);
NSLog(@"3 %@", tmp);
NSLog(@"4 %@", tmp);
NSLog(@"5 %@", tmp);
// 对象仅登录到 autoreleasepool 1次