Objective-C: слишком много рекурсии происходит с методом, перебивающим фоновую нить? - PullRequest
2 голосов
/ 02 ноября 2011

Отказ от ответственности: этот вопрос носит чисто теоретический характер, поэтому, пожалуйста, не спрашивайте меня, почему я это делаю.

Если у меня есть следующий код:

- (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

Это приводит к ошибке "слишком много рекурсии". Почему?

Я подтвердил, что код Swizzling работает через 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];
}

Это, конечно, прекрасно работает, независимо от того, как долго я делаю delayInSeconds.

1 Ответ

5 голосов
/ 02 ноября 2011

Это всего лишь предположение, но я бы предположил, что ваш стек исчерпан задолго до того, как сработает фоновая задача. Он настроен на запуск через 3,5 секунды, затем вы продолжаете и рекурсивно вызываете foo. 3,5 секунды положат тонну кадров в стек и исчерпают ее до того, как метод изменится.

Если это не так, возможно, проблема в том, как эта диспетчеризация работает с вашим циклом выполнения. Вы никогда не выходите из этого метода beginCatastrophe, поэтому у runloop никогда не будет возможности развернуться, как только вы его вызовете. Возможно, извергающаяся нить никогда не вызывается? Если вы поместите инструкцию log в fooReplacement: она вызывается?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...