views:

427

answers:

5

I'm working through a somewhat tricky iPhone crash, and it looks like the culprit is an NSString being prematurely released. I've switched on NSZombiesEnabled and can see that the NSString is a zombie at the time of the crash. However, I can't determine when the object is being released/dealloced--I've combed through my code looking for release messages being sent to this object and have set breakpoints at these spots, but they're not being hit.

I assume this may be a threading or autorelease issue given it's intermittent nature, but is there any way to hook into the objective-c runtime via the Xcode debugger to tell the exact point where an object is being released? Or is there a better way to diagnose this issue?

+4  A: 

I might not be thinking straight, but have you considered adding a release and dealloc onto your class

- (void) release 
{
  NSLog(@"Releasing");
  [super release];
}
- (void) dealloc
{
  NSLog(@"Deallocating");
  [super dealloc];
}

Incorporating Ben Gotow's comment to use an obj-c category, you end up with this:

@interface NSString (release) 
  -(void) release;
@end

@implementation NSString (release)
-(void) release
{
  NSLog(@"NSString Released!");
  [super release];
}
@end
Martin Clarke
Should have mentioned, this is an NSString.
drewh
How about subclassing NSString: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/doc/uid/20000154-397865 - the above technique would then still work.
Martin Clarke
I believe you could create an Obj-C category that overrides the existing release and dealloc functions of NSString without subclassing... Subclassing would probably be very messy.
Ben Gotow
A: 

Could you implement dealloc() and put a breakpoint in there? Looking at the stack trace from that point should tell you where and how it's being released.

Eric Petroelje
This is an NSString zombie, so I can't do a breakpoint in dealloc.
drewh
+2  A: 

you could tell the objc provider of dtrace to trigger your probe whenever -[NSString release] is called, but this will involve a little nasty hackery. NSStrings aren't actually NSStrings but are all subclasses, because of the way the class is implemented as a class cluster. Now, that's not going to get in our way; what will is that NSString doesn't have its own -release :-). You can provide your own in a category, though.

Alternatively, if it's easy for you to tell which instance of NSString is going to break, you could just set a conditional breakpoint on -[NSObject dealloc] with self==myInstance.

Graham Lee
+5  A: 

If you can reproduce the crash in the simulator, you may wish to look into using the malloc_history tool. (It has a man-page.) You need to set some environment variables: I normally set them via the "Edit Active Executable" screen in the Arguments pane, and then use the check-boxes there to enable/disable them. Make sure you disable them before debugging on the device; if enabled the program will try to write to /tmp which the sandbox doesn't allow.

I find this tool combined with NSZombie lets me track down alloc/premature-release/access-after-dealloc errors. Once NSZombie reports access to a deallocated object you can use malloc_history to work out when the object was allocated. This normally sets me on the path to working out where the problem is.

Another tool I've found invaluable is clang from the LLVM project. It's still in development, but they regularly produce binaries for MacOS-X that seem pretty stable to me. In particular, it understands the Cocoa memory-management policy. Using it is as simple as:

% cd ${DIRECTORY_CONTAINING_XCODE_PROJECT}
% xcodebuild clean
% scan-build -V xcodebuild

This will do a full build of your project and produce a report listing any obvious errors (including reference-counting screw-ups) that the tool finds.

Andrew
I second using CLang. That will often find stuff you might have easily miss, and lots of stuff it finds can lead to problems (not everything)
Kendall Helmstetter Gelner
+2  A: 

Another way to do this. Make sure to turn NSZombie on so it reports the memory address of the object that is getting the extra release. Then Run with Performance Tool->Object Allocations. This will bring up instruments. Look at the Console log as provided by Xcode organizer. Once you get the crash lookup the memory address in instruments. You will see the entire history of mallocs/frees on that object, as well as links straight into your code.

Ish