Почему не происходит сбой?

Я пытаюсь сузить ошибку до минимально воспроизводимого случая и обнаружил что-то странное.

Рассмотрим этот код:

static NSString *staticString = nil;
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    if (staticString == nil) {
        staticString = [[NSArray arrayWithObjects:@"1", @"2", @"3", nil] componentsJoinedByString:@","];
    }   

    [pool drain];

    NSLog(@"static: %@", staticString);
    return 0;
}

I Я ожидаю, что этот код выйдет из строя. Вместо этого он регистрирует:

2011-01-18 14:41:06.311 EmptyFoundation[61419:a0f] static: static: 

Однако, если я изменю NSLog () на:

NSLog(@"static: %s", [staticString UTF8String]);

, тогда выйдет из строя .

отредактируйте немного больше информации :

После осушения пула:

NSLog(@"static: %@", staticString);  //this logs "static: static: "
NSLog(@"static: %@", [staticString description]); //this crashes

Очевидно, что вызов метода для строки достаточно хорош, чтобы вызвать сбой. В таком случае, почему запись строки не приводит к ее сбою? Разве NSLog () не должен вызывать метод -description ?

Откуда берется второй "static:"? Почему не происходит сбой?


Результаты:

Кевин Баллард и Грэм Ли правы. Грэм прав, понимая, что NSLog () - это , а не , вызывающее -описание (как я ошибочно предположил), и Кевин почти определенно прав в том, что это странно проблема, связанная со стеком, при копировании строки формата и va_list вокруг.

  1. NSLogging и NSString не вызывает -описание . Грэм элегантно показал это, и если вы проследите через источники Core Foundation, которые ведут журнал, вы увидите, что это так. Любая обратная трассировка, происходящая внутри NSLog , показывает, что она вызывает NSLogv => _CFLogvEx => _CFStringCreateWithFormatAndAndArguments104Aux] [111AndArgumentsAux] [111AndArguments] [111] _CFStringAppendFormatAndArgumentsAux () (строка 5365) - это то место, где происходит вся магия. Вы можете видеть, что он вручную выполняет поиск всех замен % . Функция копирования описания вызывается только в том случае, если тип замены - CFFormatObjectType , функция описания отлична от нуля и подстановка еще не была обработана другим типом. Поскольку мы показали, что описание не копируется, разумно предположить, что NSString обрабатывается раньше (в этом случае он, вероятно, будет копировать необработанные байты), что наводит нас на мысль ...
  2. Как предполагает Кевин, здесь происходит ошибка стека. Каким-то образом указатель, который был , указывающим на автоматически выпущенную строку, заменяется на другой объект, который оказывается как NSString . Итак, он не падает. Странно. Однако, если мы изменим тип статической переменной на что-то другое, например NSArray , тогда будет вызван метод -описание , и программа выйдет из строя, как ожидалось.

Как истинно и совершенно странно. Очки достаются Кевину за то, что он наиболее прав в отношении основной причины поведения, и благодарность Грэму за исправление моего ошибочного мышления. Хотел бы я принять два ответа ...

9
задан Dave DeLong 18 January 2011 в 23:34
поделиться