2010-11-02 2 views
6

Либо мой отладчик сломан, либо есть что-то фундаментальное, что я не понимаю.Простой Objective-C перевыпуск, который * должен * сбой не сбой. Зачем?

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

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. Может ли это иметь к этому какое-то отношение?

+0

Я уже давно задавался вопросом об этом, видел ту же «проблему»/поведение. Надеюсь, кто-то может это объяснить. – Rits

+0

Сбор мусора не мгновенен? :) – willcodejavaforfood

+1

Дело не в этом. В среде сбора мусора явный вызов 'release' абсолютно ничего не делает. – Yuji

ответ

4

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

В этом случае

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

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

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

+0

Хорошо, это имеет смысл для меня. Что, если я сделал '[[NSString alloc] init];' в отличие от инициализации с постоянной строкой? В моем тестировании он по-прежнему демонстрирует то же поведение, что и при использовании константной строки, это тоже ожидается? А как насчет других случаев, когда я вижу такое же поведение, как, например, с 'NSURL' и' NSNumber'? – Jasarien

+0

И да, вы правы, строка и @ «Привет» оба указывают на одно и то же место. Это очищает это. Тем не менее, я все еще запутался в других случаях. – Jasarien

+0

'[[NSString alloc] init]' возвращает общую пустую строку, которая снова не уничтожается. Нет никакой гарантии, что каждый вызов '[[SomeClass alloc] init]' дает отдельный экземпляр. Контракт заключается в том, что, если вы следуете правилу сохранения/выпуска, программа не сбой. Не более того. – Yuji

1

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

Попробуйте

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

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

Смежные вопросы