简体   繁体   English

为什么我的代码仅在版本中以及升级到Xcode 4.6之后崩溃?

[英]Why my code crash only in release and after upgrade to Xcode 4.6?

I got a crash due to deallocating of variable that holds reference to block being executed. 由于释放包含对正在执行的块的引用的变量而导致崩溃。 Here is code example: 这是代码示例:

This is what is wrong now in release, in debug on same device runs ok, it must be run as add-hoc to crash. 这是现在发布的错误,在同一设备上的调试中可以正常运行,它必须作为临时运行才能崩溃。

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

This is defined in NSObject category: 这在NSObject类别中定义:

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

End implementation of used class: 结束使用类的实现:

@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

So I understand that the _test variable is deallocated and then probably also block while in it is deallocated? 所以我知道_test变量被释放,然后在释放时可能也会阻塞? So is that why it crashes? 那就是为什么它崩溃? But why doesn't it crash in debug, can I force somehow compiler to crash on this also in debug? 但是,为什么它在调试时不崩溃,我是否可以在调试中以某种方式强制编译器在此崩溃? Thank you. 谢谢。

Blocks capture local state. 块捕获本地状态。 In your case the block is capturing _count and self . 在您的情况下,该块正在捕获_countself In order to do that efficiently, when you create a block it initially lives on the stack, with the effect that it is safe to be used only for as long as that method doesn't return. 为了有效地做到这一点,当您创建一个块时,它最初位于堆栈中,其结果是仅在该方法不返回的情况下才可以安全使用它。 So you can pass blocks downward but you can't keep them or pass them upward without moving them to the heap. 因此,您可以向下传递块,但不能保留它们或将它们向上传递而不将它们移到堆中。 You achieve that by copy ing them (and copy is defined to act as retain if the block is already on the heap, so you don't pay for over-copying). 您可以通过copy它们来实现(如果该块已经在堆上,则copy被定义为retain ,因此您不必为过度复制付钱)。

In your case, the correct thing would be for doLater:: to copy the block (though, for the record, unnamed parameters are considered extremely poor practice ). 在您的情况下,正确的做法是使用doLater::复制块(尽管出于记录目的,未命名的参数被认为是极差的做法 )。

I'm a bit confused as to why you both assign the method to an instance variable and schedule it for a later pass it forward, but the quickest fix would be: 我对为什么都将方法分配给实例变量并计划将其调度以供以后传递给它感到有些困惑,但是最快的解决方法是:

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

As to why this appears to have become broken under 4.6: you were relying on undocumented behaviour (albeit undocumented behaviour that feels like it should be natural) so any change in toolset or OS (or indeed, no change whatsoever) is permitted to affect that. 关于为什么它在4.6下似乎已被打破:您所依赖的是未记录的行为(尽管感觉像是自然的未记录的行为),因此允许对工具集或OS进行任何更改(或者实际上,不做任何更改)都可以影响该行为。

(aside: you also seem to be reimplementing a lot of what is built into GCD; you could directly replace from:: with dispatch_after and methodToPerform: with dispatch_async , in both cases supplying dispatch_get_main_queue() as the queue). (此外:您似乎还重新实现了GCD中内置的许多功能;您可以直接用from:: methodToPerform:替换dispatch_after methodToPerform:methodToPerform:使用dispatch_async替换,在两种情况下都提供dispatch_get_main_queue()作为队列)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM