3

这里有一个类似的问题,它没有准确解释我想要什么: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;  
        }
    }];
}

但是,如果我解除了视图控制器的分配,则回调会严重访问“我”。

使“我”为空的最简单方法是什么?如果我把它作为一个 iVar,它会带回循环引用......我想?

我想我错过了一些明显的东西......

谢谢

4

3 回答 3

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并确保在您的-dealloc. 还有其他选择,但它们更复杂。

于 2012-06-05T21:16:54.433 回答
1

我根据建议做了上述事情的组合。包括消除积木。虽然,我的对象仍然没有立即释放。即我会在MyViewController 的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;  
        }
    }];
}
于 2012-06-06T10:14:22.867 回答
0

您是否正在尝试避免真正的保留周期问题?有没有一个理由不self应该简单地保留到-executeWithCompletion:完成?有没有真正的机会不会完成?

只要它真的最终会完成(即使失败)并且只要它在调用它之后释放块(可能通过将属性设置为nil),那么保留周期最终会被打破并且一切都会好起来的。

于 2012-06-06T08:22:57.030 回答