Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Objective-C over-release that *should* crash is not crashing. Why?

Either my debugger is broken or there's something fundamental that I am not understanding.

I have some very basic code in a very basic command line program that should crash. However, it is not crashing.

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

    [string release];

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

    return 0;
}

The log statement prints "Length: 5" as you would expect for a valid string. However, the string should be deallocated by that point and an exec_bad_access error should be thrown.

I have tried this code with the debugger attached and without the debugger attached - both give the same result. I have also enabled (and disabled) NSZombie, which seems to have no effect (I initially thought this was the problem, since NSZombie objects are never deallocated - but it still does not crash with NSZombie disabled).

I have breakpoints set in my local .gdbinit file to break on things such as -[NSException raise] and objc_exception_throw. I also have breakpoints set on many methods on NSZombie in order to catch them.

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

With these breakpoints set and NSZombie enabled, I should get something like [NSString length]: message sent to deallocated instance 0x100010d39 printed to the console, but I do not see this. I see the NSLog printing the length as 5.

I see similar behaviour with other classes such as NSURL and NSNumber. But some classes crash as expected, such as NSError and NSObject.

Does this have something to do with class clusters? Do they not follow the same rules in regards to memory management?

If class clusters are not related to this problem, the only other common feature I could see was that the classes which don't crash in this way are all toll-free bridged with a Core Foundation counterpart. Could this have something to do with it?

like image 495
Jasarien Avatar asked Nov 02 '10 15:11

Jasarien


2 Answers

retain/release is a contract between the API and the programmer that when you follow the rule, it doesn't crash. The contract doesn't guarantee that if you doesn't follow the rule, it does crash!

In this case,

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

just returns the same object as @"Hello" as an optimization. The constant NSString is never deallocated; as an optimization, retain and release are (I think) ignored. That's why it doesn't crash.

You can check my guess by comparing the pointer value of @"Hello" and string.

like image 87
Yuji Avatar answered Oct 04 '22 06:10

Yuji


This is an excellent example of why retain counts are a pretty useless debugging tool. It is a mistake to assume -retain and -release always add or remove 1 from the retain count and that the retain count is what you think it should be.

Try

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

That should give you a string that will behave more like the way you are expecting since you are not initialising your string from a compile time constant. However, be warned, with zombies enabled, you'll get the behaviour you expect but without zombies, the NSLog may well work. Although the string has been deallocated, the data in the object is still there in memory leaving a "ghost" that will respond correctly to some messages.

like image 37
JeremyP Avatar answered Oct 04 '22 07:10

JeremyP