简短的回答
不应该直接访问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];
}
The problem here is that self is retaining a reference to the block; meanwhile the block must retain a reference to self in order to fetch its delegate property and send the delegate a method. If everything else in your app releases its reference to this object, its retain count won't be zero (because the block is pointing to it) and the block isn't doing anything wrong (because the object is pointing to it) and so the pair of objects will leak into the heap, occupying memory but forever unreachable without a debugger. Tragic, really.
这种情况可以通过这样做来轻松解决:
id progressDelegate = self.delegate;
self.progressBlock = ^(CGFloat percentComplete) {
[progressDelegate processingWithProgress:percentComplete];
}
In this code, self is retaining the block, the block is retaining the delegate, and there are no cycles (visible from here; the delegate may retain our object but that's out of our hands right now). This code won't risk a leak in the same way, because the value of the delegate property is captured when the block is created, instead of looked up when it executes. A side effect is that, if you change the delegate after this block is created, the block will still send update messages to the old delegate. Whether that is likely to happen or not depends on your application.
即使你对这种行为很冷静,你仍然不能在你的情况下使用这个技巧:
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];
};
这个解决方案避免了保留周期,并且总是调用当前委托。
If you can't change the block, you could deal with it. The reason a retain cycle is a warning, not an error, is that they don't necessarily spell doom for your application. If MyDataProcessor is able to release the blocks when the operation is complete, before its parent would try to release it, the cycle will be broken and everything will be cleaned up properly. If you could be sure of this, then the right thing to do would be to use a #pragma to suppress the warnings for that block of code. (Or use a per-file compiler flag. But don't disable the warning for the whole project.)
您还可以考虑使用上面类似的技巧,将引用声明为弱引用或未保留引用并在块中使用。例如:
__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没有在其他任何地方使用)。
什么是最好的取决于哪些代码可以更改,哪些代码不能更改。但希望这能给你一些如何进行的想法。