0

由于释放了对正在执行的块的引用的变量,我遇到了崩溃。这是代码示例:

这是现在发布的问题,在同一设备上的调试运行正常,它必须作为附加运行才能崩溃。

- (void)test {
    _test = [self doLater:^{
      _count++;
       [self test];
   } :3];
}

这是在 NSObject 类别中定义的:

- (DoLaterProcess *)doLater:(void (^)())method :(double)delay {
    return [[DoLaterProcess new] from:method :delay];
}

使用类的结束实现:

@implementation DoLaterProcess {
    id _method;
    BOOL _stop;
}

- (void)methodToPerform:(void (^)())methodToInvoke {
    if (_stop)return;
    if (NSThread.isMainThread) methodToInvoke();
    else [self performSelectorOnMainThread:@selector(methodToPerform:)      withObject:methodToInvoke waitUntilDone:NO];
}

- (DoLaterProcess *)from:(void (^)())method:(NSTimeInterval)delay {
    [self performSelector:@selector(methodToPerform:) withObject:method afterDelay:delay];
    _method = method;
    return self;
}

- (void)stop {
    _stop = YES;
    [NSObject cancelPreviousPerformRequestsWithTarget:self     selector:@selector(methodToPerform:) object:_method];
}

@end

所以我知道 _test 变量被释放,然后可能在它被释放时也被阻塞?这就是它崩溃的原因吗?但是为什么它不会在调试中崩溃,我可以强制编译器在调试中也崩溃吗?谢谢你。

4

1 回答 1

1

块捕获本地状态。在您的情况下,该块正在捕获_countself. 为了有效地做到这一点,当您创建一个块时,它最初存在于堆栈中,其效果是只要该方法不返回,就可以安全地使用它。因此,您可以向下传递块,但不能保留它们或向上传递它们而不将它们移动到堆中。copy您可以通过ing 来实现这一点(并且copy被定义为retain好像该块已经在堆上,因此您无需为过度复制付费)。

在您的情况下,正确的做法是doLater::复制块(尽管,为了记录,未命名的参数被认为是非常糟糕的做法)。

我有点困惑为什么你们都将方法分配给实例变量并安排它以供以后传递它,但最快的解决方法是:

- (DoLaterProcess *)from:(void (^)())method:(NSTimeInterval)delay {
    method = [method copy];
    [self performSelector:@selector(methodToPerform:) withObject:method afterDelay:delay];
    _method = method;
    return self;
}

至于为什么这似乎已在 4.6 下被破坏:您依赖于未记录的行为(尽管感觉应该是自然的未记录的行为),因此允许工具集或操作系统的任何更改(或者实际上,没有任何更改)影响.

(此外:您似乎还重新实现了 GCD 中内置的许多内容;您可以直接替换from::withdispatch_aftermethodToPerform:with dispatch_async,在这两种情况下都dispatch_get_main_queue()作为队列提供)。

于 2013-02-05T21:10:00.447 回答