ARC也有其弱点,最大的一个就是循环保留。此外,LLVM的静态分析器不能检测循环保留。
当两个对象对彼此都有所有权时就会发生循环保留。一个经典的现实场景就是子视图控制器拥有指向父视图控制器的强指针,如图5-2所示。
为了避免这种循环保留,可以使用__weak
(或者__unsafe_unretained
)所有权修饰符。一个弱引用可以确保指针的引用计数的值不会增加,如图5-3所示。
如果你学会了使用所有权而不是release/retain
,就可以在大多数情况下避免出现循环保留,当然block除外。在一定程度上block和ARC是很难兼容的。
block是可以捕获上下文的特殊代码块。也就是说,任何在block外可以访问的变量都可以在block中访问。捕获上下文是新的设计模式,第22章会详细介绍。当block“捕获”了上下文,它就会为其作用域内的每个标量变量创建一个副本。其中,这个作用域内每个Objective-C对象都会被保留。有一种常见的错误:通过self
拥有一个block,而你又在block中修改了实例变量,这时就会出错。
会引发循环保留的代码
1234 | self.myBlock = ^(NSString* returnedString) { self.labelControl.text = returnedString; }; |
这段代码展示了同时使用ARC与block的一个典型陷阱。self
保留了block,而block又捕获(保留)了self
。这样就会引发循环保留。非常微妙,也很危险。
为了避免这种情况,需要在block中捕获未保留的self
引用。在没有ARC时,我们可以使用__block
关键字和__unsafe_unretained
来复制一个未保留的引用副本,并在block中使用这个引用。
使用__block
避免循环保留(无ARC)
12345 | __block id safeSelf = self; self.myBlock = ^(NSString* returnedString) { safeSelf.labelControl.text = returnedString; }; |
ARC会改变__block
的语义,因此不应使用它。在ARC中,__block
引用会被保留而不是被复制,这意味着在ARC环境中前面的代码仍然会引发循环保留。正确的办法是使用__weak
(或者unsafe_unretained
)引用,如下面的代码所示:
使用__weak
避免循环保留(有ARC)
1234567 | __weak id safeSelf = self; //iOS 5+ //__unsafe_unretained id safeSelf = self; //iOS 4+ self.myBlock = ^(NSString* returnedString) { safeSelf.labelControl.text = returnedString; } |
联系客服