__weak & autoreleasepool

__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表中删除。删除操作执行为以下内容:

  1. 从weak表中获取废弃对象的地址为键值的记录
  2. 将包含在记录中所有附有__weak修饰符的变量的地址,赋值为nil
  3. 从weak表中删除该纪录
  4. 从引用计数表中删除废弃对象的地址为键值的记录

由此可知,当大量使用附有__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次
 * /
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章