retain/release debugging

While I’m on the topic of debugging, here’s some code I use when I tracking down a retain/release bug:

#if 1
- (id)retain {
    NSUInteger oldRetainCount = [super retainCount];
    id result = [super retain];
    NSUInteger newRetainCount = [super retainCount];
    printf("%s<%p> ++retainCount: %lu => %lu\n", [[self className] UTF8String], self, oldRetainCount, newRetainCount);
    printf("%s\n", [[[NSThread callStackSymbols] description] UTF8String]);
    return result;
}

- (void)release {
    NSUInteger oldRetainCount = [super retainCount];
    BOOL gonnaDealloc = oldRetainCount == 1;
    if (gonnaDealloc) {
        printf("%s<%p> --retainCount: 1 => 0 (gonna dealloc)\n", [[self className] UTF8String], self);
        printf("%s\n", [[[NSThread callStackSymbols] description] UTF8String]);
    }
    [super release];
    if (!gonnaDealloc) {
        NSUInteger newRetainCount = [super retainCount];
        printf("%s<%p> --retainCount: %lu => %lu\n", [[self className] UTF8String], self, oldRetainCount, newRetainCount);
        printf("%s\n", [[[NSThread callStackSymbols] description] UTF8String]);
    }
}
#endif

Stick it into a class and out will print a nice stack trace for each invocation of retain and release for each instance of your class, helping you spot imbalances.

I keep it in an #if 1 block so it’s easy to turn on and off across debugging runs (usually I paste this code into two or three classes at a time to catch subtle interactions that are causing the bug).

There are higher-level tools for debugging this stuff, but this is quick and easy to inject into specific troublesome classes, find the issue and then back it out again. Usually my use for these don’t even make it into version control.

Plus, its printouts are real pretty thanks to +[NSThread callStackSymbols].

debugging Mar 16 2011