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 a开发者_如何学Pythonll toll-free bridged with a Core Foundation counterpart. Could this have something to do with it?
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
.
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.
精彩评论