Простой переиздание Objective-C, которое * следует * аварийно завершить, не дает сбоя. Зачем? - PullRequest
6 голосов
/ 02 ноября 2010

Либо мой отладчик неисправен, либо есть нечто фундаментальное, чего я не понимаю.

У меня есть очень простой код в очень простой программе командной строки, который должен аварийно завершить работу. Тем не менее, это не сбой.

int main (int argc, const char * argv[])
{
    NSString *string = [[NSString alloc] initWithString:@"Hello"];

    [string release];

    NSLog(@"Length: %d", [string length]);

    return 0;
}

Оператор журнала печатает «Длина: 5», как и следовало ожидать для правильной строки. Однако строка должна быть освобождена этой точкой, и должна быть выдана ошибка exec_bad_access.

Я пробовал этот код с подключенным отладчиком и без подключенного отладчика - оба дают одинаковый результат. Я также включил (и отключил) NSZombie, что, похоже, не дает никакого эффекта (я изначально думал, что это проблема, поскольку NSZombie объекты никогда не освобождаются - но он по-прежнему не падает при отключенном NSZombie).

У меня установлены точки останова в локальном файле .gdbinit для таких вещей, как -[NSException raise] и objc_exception_throw. У меня также есть точки останова во многих методах на NSZombie, чтобы их перехватить.

fb -[NSException raise]
fb -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:]
fb -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:]

#define NSZombies
# this will give you help messages.  Set to NO to turn them off.
set env MallocHelp=YES
# might also be set in launch arguments.
set env NSZombieEnabled=YES
set env NSDeallocateZombies=NO
set env MallocCheckHeapEach=100000
set env MallocCheckHeapStart=100000
set env MallocScribble=YES
set env MallocGuardEdges=YES
set env MallocCheckHeapAbort=1

set env CFZombie 5

fb -[_NSZombie init]
fb -[_NSZombie retainCount]
fb -[_NSZombie retain]
fb -[_NSZombie release]
fb -[_NSZombie autorelease]
fb -[_NSZombie methodSignatureForSelector:]
fb -[_NSZombie respondsToSelector:]
fb -[_NSZombie forwardInvocation:]
fb -[_NSZombie class]
fb -[_NSZombie dealloc]

fb szone_error
fb objc_exception_throw

С этими установленными точками останова и включенным NSZombie я должен напечатать что-то вроде [NSString length]: message sent to deallocated instance 0x100010d39 на консоли, но я этого не вижу. Я вижу NSLog, печатающий длину как 5.

Я вижу похожее поведение с другими классами, такими как NSURL и NSNumber. Но некоторые классы аварийно завершаются, как, например, NSError и NSObject.

Это как-то связано с кластерами классов? Разве они не следуют тем же правилам в отношении управления памятью?

Если кластеры классов не связаны с этой проблемой, единственная другая общая черта, которую я мог видеть, состояла в том, что классы, которые не терпят крах таким образом, все бесплатно соединяются мостом с аналогом Core Foundation. Может ли это быть как-то связано с этим?

Ответы [ 2 ]

4 голосов
/ 02 ноября 2010

retain / release - это контракт между API и программистом, который, если вы следуете правилу, не падает.Контракт не гарантирует, что если вы не будете следовать правилу, он действительно рухнет!

В этом случае

[[NSString alloc] initWithString:@"Hello"]

просто возвращает тот же объект, что и @"Hello" в качестве оптимизации.Константа NSString никогда не освобождается;в качестве оптимизации retain и release (я думаю) игнорируются.Вот почему он не падает.

Вы можете проверить мои предположения, сравнив значения указателей @"Hello" и string.

1 голос
/ 02 ноября 2010

Это отличный пример того, почему счетчики сохранения являются довольно бесполезным инструментом отладки.Ошибочно полагать, что -retain и -release всегда добавляют или удаляют 1 из счетчика остатков, а количество остатков - это то, что, как вы думаете, должно быть.

Попробуйте

NSString *string = [[NSString alloc] initWithFormat:@"Hello %d", argc];

должен дать вам строку, которая будет вести себя больше, чем вы ожидаете, так как вы не инициализируете свою строку из постоянной времени компиляции.Однако, имейте в виду, что при включенных зомби вы получите ожидаемое поведение, но без зомби NSLog вполне может сработать.Хотя строка была освобождена, данные в объекте все еще находятся в памяти, оставляя «призрак», который будет правильно отвечать на некоторые сообщения.

...