简短答案
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保留了对该块的引用;同时,该块必须保留对self的引用,以获取其委托属性并向委托发送方法。如果您应用中的所有其他内容都释放了对该对象的引用,则其保留计数将不会为零(因为该块指向它),并且该块没有做错任何事情(因为该对象指向了它),因此这对对象将泄漏到堆中,占用内存,但如果没有调试器,将永远无法访问。真的很悲惨。
通过执行以下操作可以轻松解决该情况:
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
在此代码中,self保留了块,block保留了委托,并且没有循环(从此处可见;委托可以保留我们的对象,但现在这已不复存在)。该代码不会以相同的方式冒着泄漏的风险,因为委托属性的值是在创建块时捕获的,而不是在执行时查找的。副作用是,如果在创建此块之后更改委托,则该块仍将向旧委托发送更新消息。这是否可能发生取决于您的应用程序。
即使您对这种行为很酷,也无法在您的情况下使用该技巧:
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
能够在操作完成后释放块,则在其父项尝试释放块之前,该循环将被中断,并且所有内容将被正确清理。如果可以确定,那么正确的做法是使用a #pragma
禁止显示该代码块的警告。(或使用每个文件的编译器标志。但是请不要对整个项目禁用警告。)
您也可以使用上面的类似技巧,声明一个引用弱或未保留的引用,并在块中使用它。例如:
__weak MyDataProcessor *dp = self; // OK for iOS 5 only
__unsafe_unretained MyDataProcessor *dp = self; // OK for iOS 4.x and up
__block MyDataProcessor *dp = self; // OK if you aren't using ARC
self.progressBlock = ^(CGFloat percentComplete) {
[dp.delegate myAPI:dp isProcessingWithProgress:percentComplete];
}
上面的所有三个都将为您提供引用,但不会保留结果,尽管它们的行为略有不同:__weak
释放对象时,将尝试将引用归零;__unsafe_unretained
会给您留下无效的指针;__block
实际上会添加另一个间接级别,并允许您从块内更改引用的值(在这种情况下无关紧要,因为dp
在其他任何地方都没有使用)。
什么是最好的,你不能将取决于你能够改变什么代码和什么。但是希望这能给您一些有关如何进行的想法。