Objective-C:方法在后台线程上旋转时会出现太多递归?

Objective-C:方法在后台线程上旋转时会出现太多递归?,objective-c,Objective C,免责声明:这个问题纯粹是理论问题,所以请不要问我为什么要这样做 如果我有以下代码: - (void) beginCatastrophe { double delayInSeconds = 3.5; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_global_queu

免责声明:这个问题纯粹是理论问题,所以请不要问我为什么要这样做

如果我有以下代码:

- (void) beginCatastrophe {
    double delayInSeconds = 3.5;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_global_queue(0, 0), ^(void){
        Class cls = [self class];
        IMP replacement = class_getMethodImplementation(cls, @selector(fooReplacement:));
        Method fooMethod = class_getInstanceMethod(cls, @selector(foo:));
        method_setImplementation(fooMethod, replacement);
    });

    [self foo:1];
}

- (void) fooReplacement:(unsigned) x {}

- (void) foo:(unsigned) x {
    [self foo:++x];
}
在我代码的其他地方,我调用
-beginCatastrophe

这会导致“太多递归”错误。为什么?

我已确认Swizzing代码在
2
秒后工作,但不会超过 那个

但是,如果我这样做:

- (void) beginCatastrophe {
    double delayInSeconds = 3.5;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_global_queue(0, 0), ^(void){
        Class cls = [self class];
        IMP replacement = class_getMethodImplementation(cls, @selector(fooReplacement:));
        Method fooMethod = class_getInstanceMethod(cls, @selector(foo:));
        method_setImplementation(fooMethod, replacement);
    });

    [self foo:nil];
}


- (void) fooReplacement:(id) x { 
    printf("%s", _cmd); 
}

- (void) foo:(id) x {
    [self performSelector:_cmd withObject:x afterDelay:0.00001];
}

当然,无论我将
delayUnseconds
设置多长时间,这都可以正常工作

这只是一个猜测,但我猜您的堆栈在启动后台任务之前就已经耗尽了。您将它设置为在3.5秒后启动,然后继续并递归调用foo。3.5秒将在堆栈上放置一吨帧,并在该方法被旋转之前将其耗尽


如果不是这样,那么可能是这个调度如何与您的runloop一起工作的问题。您永远不会退出beginCatastrophe方法,因此一旦调用它,runloop就永远不会有机会打开。也许swizzling线程从未被调用过?如果在fooReplacement中放入日志语句:会调用它吗

我的猜测是,您没有按照预期的方式完成替换,替换前版本的foo:肯定会再次出现,直到它不能再出现为止。@DanielRHicks替换代码可以工作-这只是运行时应用
IMP
交换的能力的问题。更重要的是,编译器可能“短路”在某些情况下,在同一类中进行调用,并且很可能会缩短方法本身中的递归调用。尝试将您的foos移动到一个单独的类中。(如果替换代码真的有效,您将不会得到无限递归。)这可能会有帮助。。。我想你说得对,我的堆栈已经耗尽了。因此,这将在不同的机器上执行不同的操作。我明白了。当然,如果控件在之后立即从
dispatch\u返回,那么将不会发生替换,并且foo将无限重复出现。规范对
dispatch\u after
是否立即返回不明确。它可能会等待2秒,但会立即返回,等待时间更长。这段代码不起作用的原因有很多,但很少有人解释它为什么会起作用。@DanielRHicks
dispatch\u after
会立即返回。@JacobRelkin--只是规范说“函数等待”或类似的话,这很容易被解释为它不会立即返回。