例如:
[self.contentWrapperView addGestureRecognizer:
[UITapGestureRecognizer recognizerWithHandler:^(UIGestureRecognizer *sender,
UIGestureRecognizerState state,
CGPoint location) {
if (self.customEditing) {
[self setEditingMode:NO Animated:YES];
}
}]];
其中contentWrapperView
是self
上的强性质,假设contentWrapperView
对识别块有强引用。在块中使用self
会导致保留循环吗?这是我唯一不太明白的部分。
是的,这将导致一个保留循环。
如果你关心的是
__weak id weakSelf = self;
[self.contentWrapperView addGestureRecognizer:[UITapGestureRecognizer recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
if ([weakSelf customEditing]) {
[weakSelf setEditingMode:NO Animated:YES];
}
}]];
从保留周期的实际意义来考虑它:你所拥有的一个对象对你自己的保留。
在Cocoa和Cocoa- touch的术语中,这意味着您强烈拥有的任何变量不能反过来强烈拥有您。这通常是通过声明一个需要拥有其父weak
、unsafe_unretained
或assign
的属性来解决的。块就像闭包一样,捕获对其中每个变量的const或保留引用(显然,__block
限定符使情况变得更加复杂),这会使情况变得复杂。
我不从代码块的角度来看待这个问题,而是尝试将其抽象为一个包含两个类的示例,这对于使用OO语言的人来说通常更容易理解:
假设我有一个类MyImportantObject
,它需要与另一个类MyWorkerClass
做一些工作。我们通常需要MyWorkerClass
的强引用,因为我们可能希望它保留下来,以便以后可以做更多的工作,或者继续一遍又一遍地调用相同的工作方法:
@interface MyImportantObject : NSObject
@property (nonatomic, strong) MyWorkerClass *workerObj;
@end
反过来,我们的worker需要一个稳定的对MyImportantObject
的所有属性的引用来工作(否则它不能工作!)那么引用self
的块做了什么,而不是对每个变量使用const或保留引用,它们对父变量使用保留引用!@interface MyWorkerClass : NSObject
@property (nonatomic, strong) MyImportantObject *workerObj;
@end
这意味着如果你试图使用MyImportantObject
的工作对象,它们会相互保留,并且都不会被正确地释放!巨大的禁忌。
通过将self
分流到__weak
(或MRC下的__block
)指针,我们得到的引用看起来像这样:
@interface MyWorkerClass : NSObject
@property (nonatomic, weak) MyImportantObject *workerObj;
@end
不再有保留周期,每个人都很高兴,当父代走恐龙的路时,每个人都得到了适当的分配。
但是还有一个难题:我们该如何处理weak
引用?毕竟,让我们假设好的编译器出现了,用一个快速的-release
把self
从你下面拉出来。因为那个归零弱指针,你现在有一个nil指针在你的代码块中乱转。这可能导致它什么都不做,而你坐在那里挠头想知道为什么。这导致了一种我称之为"弱-强舞蹈"的发明。
J_mcnally的例子缺少了舞蹈中的一个关键步骤,即自我指针的"重新强化"。但是,为什么我们要这样做在我们费尽周折避免保留循环之后?这是因为X的强拷贝不是由块保留,而是由块的作用域保留。在英语中,这意味着我们的强self
只会在到达块的末尾时释放,从而不仅保证了一个安全的内存周期,而且它不会从我们下面被释放。正确的修复方法如下:
__weak id weakSelf = self;
[self.contentWrapperView addGestureRecognizer:[UITapGestureRecognizer recognizerWithHandler:^(UIGestureRecognizer *sender, UIGestureRecognizerState state, CGPoint location) {
id strongSelf = weakSelf;
if ([strongSelf customEditing]) {
[strongSelf setEditingMode:NO Animated:YES];
}
//Do some other stuff before strongSelf passes out of scope.
}]];