这里有一个类似的问题,它没有准确解释我想要的内容:Objective C Blocks as Async-callbacks & BAD ACCESS
我有一个视图控制器,它使用异步回调调用服务。回调是使用一个块完成的,该块引用视图控制器上的变量来填充它们。
看起来像这样:
- (void) loadData {
__block MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
me.data = result;
}
}];
}
但是,如果我取消分配视图控制器,那么回调将很难访问“我”。
使'我'为NULL的最简单方法是什么?如果我把它作为iVar,那么它会带回循环参考...我想?
我想我错过了一些明显的事情......
由于
答案 0 :(得分:5)
您的目标是iOS 5.0或更高版本(或Mac OS X 10.7或更高版本)吗?如果是这样,您可以使用ARC和__weak
变量(而不是__block
变量)。当引用的对象被释放时,这将自动归零。您的代码看起来像
- (void)loadData {
__weak MyViewController *me = self;
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
if (!error) {
MyViewController *strongMe = me; // load __weak var into strong
if (strongMe) {
strongMe.data = result;
}
}
}];
}
如果您需要支持较旧的操作系统,那么您需要找到不同的解决方案。一种解决方案是继续让块保留self
。如果保证服务执行完成块(然后释放它),这将只产生一个临时循环,当运行完成块时它将自动中断。或者,如果您有某种方法取消服务(以保证在取消后无法调用阻止的方式),您可以坚持使用__block
,并确保取消{{1}中的服务}。还有其他选择,但它们更复杂。
答案 1 :(得分:1)
我从建议中做了上述事情的组合。包括nilling块。虽然,我的物品仍未立即释放。即我在MyallController的dealloc上放置一个断点,如果没有__block变量,它将在稍后的某个时间点被调用(可能是由于异步连接),有时根本不会。
代码相当复杂 - 所以我想还有其他事情可以使它无法正常工作。
我还做了,使用了Mike Ash的MAZeroingWeakRef,我猜这与使用__weak相同 - @KevinBallard建议。
以下是我如何实施它,它似乎正在工作。在处理我想要的视图控制器时立即调用Dealloc。我无法让它崩溃......而且我已经放入了日志评论,我已经可以看到我正在躲避子弹。
- (void) loadData {
__block MAZeroingWeakRef *zeroWeakRef = [[MAZeroingWeakRef alloc] initWithTarget:self];
[zeroWeakRef setCleanupBlock: ^(id target) {
[zeroWeakRef autorelease];
}];
[self.service executeWithCompletion:^(NSArray *result, NSError *error) {
MyViewController *me = [zeroWeakRef target];
if (!me) {
DULog(@"dodged a bullet");
}
if (!error) {
me.data = result;
}
}];
}
答案 2 :(得分:0)
是否存在您要避免的真正保留周期问题?是否有理由self
在完成-executeWithCompletion:
之前不应该保留nil
?有没有真正的机会不能完成?
只要它真的最终会完成(即使失败)并且只要它在调用它之后释放块(可能通过将属性设置为{{1}}),那么保留周期最终会被破坏并且一切都会好的。