__weak修饰符相对于__strong修饰,是为了解决循环引用的。
@interface Test: NSObject
{
id __strong obj_;
}
- (void)setObject:(id __strong)obj;
@end
@implement Test
- (id)init {
self = [super init];
return self;
}
- (void)setObject:(id __storng)obj {
obj_ = obj;
}
@end
{
id test0 = [Test new]; /* 对象A */
/*
* test0持有Test对象A的强引用
* /
id test1 = [Test new]; /* 对象B */
/*
* test1持有Test对象B的强引用
* /
[test0 setObject:test1];
/*
* Test对象A的obj_成员变量持有Test对象B的强引用
* 此时,持有Test对象B的强引用的变量为Test对象A的obj_和test1
*/
[test1 setObject:test0];
/*
* Test对象B的obj_成员变量持有Test对象A的强引用
* 此时,持有Test对象A的强引用的变量为Test对象B的obj_和test0
*/
} /*
* 因为test0变量超出其作用域,强引用失效,自动释放Test对象A
* 因为test1变量超出其作用域,强引用失效,自动释放Test对象B
* 持有Test对象A的强引用的变量为Test对象B的obj_
* 持有Test对象B的强引用的变量为Test对象A的obj_
* 发生内存泄漏
*/
上面的例子就是一个明显的引用循环。可以用weak来解决
@interface Test: NSObject
{
id __strong obj_;
} /*
* 相互不拥有
* /
weak还有以下2点特性
- 1.若附有__weak修饰符的变量所引用的对象被废弃,则将nil赋值给该变量
- 2.使用__weak修饰符的变量,即是注册到autoreleasepool中的对象
1
{
id __weak obj1 = obj;
}
/*上述语句在编译器中的模拟源码*/
id obj1;
objc_initWeak(&obj1, obj)
objc_destoryWeak(&obj1)
objc_initWeak先初始化变量,然后再作用域结束时调用objc_destoryWeak释放该变量。
而objc_initWeak(&objc1,obj)的源码可以如下表示
obj1 = 0;
objc_storeWeak(&obj1,obj);
objc_storeWeak函数把第二参数的赋值对象的地址作为键值,将第一参数的附有 __weak修饰符的变量的地址注册到weak表中,如果第二参数为0,则把变量的地址从weak表中删除。删除操作执行为以下内容:
- 从weak表中获取废弃对象的地址为键值的记录
- 将包含在记录中所有附有__weak修饰符的变量的地址,赋值为nil
- 从weak表中删除该纪录
- 从引用计数表中删除废弃对象的地址为键值的记录
由此可知,当大量使用附有__weak修饰符的变量,则会消耗很多CPU资源,因为会进行如上操作。所以比较好的策略是只在避免循环引用时使用__weak修饰符。
2.
id __weak obj1 = obj;
NSLog(@"%@", obj1);
/*编译器的模拟代码*/
id obj1;
objc_initWeak(&obj1, obj);
id tmp = objc_loadWeakRetained(&obj1);
objc_autorelease(tmp);
NSLog(@"%@", tmp);
objc_destoryWeak(&obj1);
从上面的代码可知,objc_loadWeakRetained函数取出__weak修饰符变量所引用的对象并retain,objc_autorelease函数将对象注册到autoreleasepool中。
因此每次使用附有__weak修饰符的对象都会在autoreleasepool中创建对象,所以大量使用时,建议先暂时赋值给 __strong修饰符的变量后再使用。
id __weak o = obj;
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
NSLog(@"%@", o);
/*
* obj对象会在autoreleasepool中注册5次
* /
id __weak o = obj;
id tmp = o;
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
NSLog(@"%@", tmp);
/*
* obj对象在autoreleasepool中仅注册1次
* /