块和异步回调,dealloc对象 - 需要使块无效

时间:2012-06-05 21:13:06

标签: objective-c asynchronous dealloc block nszombie

这里有一个类似的问题,它没有准确解释我想要的内容: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,那么它会带回循环参考...我想?

我想我错过了一些明显的事情......

由于

3 个答案:

答案 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}}),那么保留周期最终会被破坏并且一切都会好的。