简短回答
不要直接访问self
,而应该通过一个不会被保留的引用间接访问它。如果你没有使用自动引用计数(ARC),可以这样做:
__block MyDataProcessor *dp = self
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete]
}
__block
关键字标记了变量,可以在块内部进行修改(我们不这样做),但是当块被保留时它们也不会自动保留(除非您使用ARC)。如果这样做,您必须确信,在释放 MyDataProcessor 实例后,没有其他内容会尝试执行该块。(鉴于您的代码结构,那不应该成为问题。)
阅读更多关于 __block
的信息。
如果您正在使用 ARC,则
__block
的语义会发生变化,并且引用将会被保留,在这种情况下,您应该将其声明为
__weak
。
长答案
假设您有以下代码:
self.progressBlock = ^(CGFloat percentComplete) {
[self.delegate processingWithProgress:percentComplete]
}
这里的问题在于self保留了对block的引用;同时,为了获取其委托属性并向委托发送方法,该块必须保留对self的引用。如果应用程序中的所有其他内容都释放了对此对象的引用,则其保留计数不会为零(因为块指向它),而块没有做错任何事情(因为对象指向它),因此这一对对象将泄漏到堆中,占用内存但永远无法访问,除非使用调试器。真是悲惨。
这种情况可以通过进行以下操作轻松解决:
id progressDelegate = self.delegate
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete]
}
在这段代码中,self 保留了 block,block 保留了 delegate,并且没有循环引用(从这里看不到。 delegate 可能会保留我们的对象,但我们目前无法控制)。这段代码不会以同样的方式冒泡风险,因为在创建 block 时捕获了 delegate 属性的值,而不是在执行时查找。一个副作用是,如果在创建此块之后更改了 delegate,则该块仍将向旧的 delegate 发送更新消息。这是否可能发生取决于您的应用程序。
即使您对这种行为感到满意,您仍然不能在您的情况下使用该技巧:
self.dataProcessor.progress = ^(CGFloat percentComplete) {
[self.delegate myAPI:self isProcessingWithProgress:percentComplete]
}
在这里,你直接将 self
传递给了委托,在方法调用中,所以你必须在某个地方获取它。如果你对块类型的定义有控制权,最好的方法是将委托作为参数传递到块中:
self.dataProcessor.progress = ^(MyDataProcessor *dp, CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete]
}
这个解决方案避免了保留环的问题,并且始终调用当前代理。
如果您无法更改块,可以处理它。保留环是警告而不是错误的原因是,它们并不一定会给您的应用程序带来灾难。如果MyDataProcessor能够在操作完成之前释放块,而其父级尝试释放它之前,循环将被打破,一切都将得到适当的清理。如果您可以确定这一点,那么正确的做法是使用#pragma来抑制该代码块的警告(或使用每个文件的编译器标志。但不要禁用整个项目的警告)。
您还可以尝试使用类似上面的技巧,声明一个弱引用或未保留的引用,并在块中使用它。例如:
__weak MyDataProcessor *dp = self
__unsafe_unretained MyDataProcessor *dp = self
__block MyDataProcessor *dp = self
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete]
}
所有这三个都会给你一个引用,但不会保留结果,尽管它们的行为略有不同:
__weak
在对象释放时会尝试将引用清零;
__unsafe_unretained
会使你得到一个无效指针;
__block
实际上会添加另一层间接性,并允许你从块内更改引用的值(在这种情况下不相关,因为
dp
在其他地方没有使用)。
最好的方法取决于你能够更改哪些代码以及不能更改哪些代码。但希望这给你一些关于如何继续的想法。
__weak typeof(self) weakSelf = self;
来使代码更具可移植性。 - stepmuel@weakify(..)
和@strongify(...)
,让你在块中以非保留方式使用self
。 - user816328