Skip to content

Instantly share code, notes, and snippets.

@gnachman
Created December 8, 2012 07:56
Show Gist options
  • Save gnachman/4239174 to your computer and use it in GitHub Desktop.
Save gnachman/4239174 to your computer and use it in GitHub Desktop.
Swizzle retain/release/autorelease to debug busted reference counting
// Use with https://github.com/rentzsch/jrswizzle
// MIT license
// After your program dies because with a zombie, examine [theBigLog objectForKey:@"0x12345"], subbing the address of the thing that died. You can get stack traces referred to in the logs by looking up the "stack-12345" numbers in the same dict. To track a particular object, add [NSValue valueWithPointer:myobject] to debugList in a @synchronized block.
@interface ClassToDebug : NSColor
@end
@interface ClassToDebug (DebugRefs)
@end
static NSMutableSet *debugList;
NSMutableDictionary *theBigLog;
@implementation ClassToDebug (DebugRefs)
- (bool)shouldDebugRefs {
@synchronized([NSObject class]) {
return [debugList containsObject:[NSValue valueWithPointer:self]];
}
}
- (void)logDebugRefs:(NSString *)msg {
@synchronized([NSObject class]) {
NSString *key = [NSString stringWithFormat:@"%p", self];
NSMutableString *s = [theBigLog objectForKey:key];
if (!s) {
s = [[NSMutableString alloc] init];
[theBigLog setObject:s forKey:key];
}
[s appendString:msg];
}
}
- (NSString *)debugCallStack {
NSString *syms = [[NSThread callStackSymbols] description];
NSUInteger hash = [syms hash];
NSString *key = [NSString stringWithFormat:@"stack-%llu", hash];
@synchronized([NSObject class]) {
if (![theBigLog objectForKey:key]) {
[theBigLog setObject:syms forKey:key];
}
}
return key;
}
- (id)debugRefsRetain {
id rv = [self debugRefsRetain];
if ([self shouldDebugRefs]) {
[self logDebugRefs:[NSString stringWithFormat:@"Retain %@ from %@ sending rc to %d\n", self, [self debugCallStack], (int)[self retainCount]]];
}
return rv;
}
- (oneway void)debugRefsRelease {
if ([self shouldDebugRefs]) {
[self logDebugRefs:[NSString stringWithFormat:@"Release %@ from %@ sending rc to %d\n", self, [self debugCallStack], (int)[self retainCount]-1]];
if ([self retainCount] == 1) {
@synchronized([NSObject class]) {
[debugList removeObject:[NSValue valueWithPointer:self]];
}
}
}
[self debugRefsRelease];
}
- (id)debugRefsAutorelease {
id rv = [self debugRefsAutorelease];
if ([self shouldDebugRefs]) {
[self logDebugRefs:[NSString stringWithFormat:@"Autorelease %@ from %@ sending rc to %d\n", self, [self debugCallStack], (int)[self retainCount]]];
}
return rv;
}
@end
void DoSwizzle() {
debugList = [[NSMutableSet alloc] init];
theBigLog = [[NSMutableDictionary alloc] init];
NSError *e = nil;
[ClassToDebug jr_swizzleMethod:@selector(retain) withMethod:@selector(debugRefsRetain) error:&e];
assert(!e);
[ClassToDebug jr_swizzleMethod:@selector(release) withMethod:@selector(debugRefsRelease) error:&e];
assert(!e);
[ClassToDebug jr_swizzleMethod:@selector(autorelease) withMethod:@selector(debugRefsAutorelease) error:&e];
assert(!e);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment