Created
August 14, 2012 18:28
-
-
Save nevyn/3351509 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2012-08-14 21:37:00.234 TCMemoryTools[10614:303] Todo: overload initWithThing: | |
2012-08-14 21:37:00.236 TCMemoryTools[10614:303] +[Base @ 0x100003508 allocWithZone:], RC -1 | |
2012-08-14 21:37:00.236 TCMemoryTools[10614:303] -[Child @ 0x102400030 retain], RC 1 | |
2012-08-14 21:37:00.237 TCMemoryTools[10614:303] -[Child @ 0x102400030 release], RC 2 | |
2012-08-14 21:37:00.237 TCMemoryTools[10614:303] -[Child @ 0x102400030 retain], RC 1 | |
2012-08-14 21:37:00.237 TCMemoryTools[10614:303] -[Child @ 0x102400030 release], RC 2 | |
2012-08-14 21:37:00.238 TCMemoryTools[10614:303] Hello, World! <__TCMemoryProxy_Child: 0x102400030> | |
2012-08-14 21:37:00.238 TCMemoryTools[10614:303] -[Child @ 0x102400030 release], RC 1 | |
2012-08-14 21:37:00.238 TCMemoryTools[10614:303] -[Child @ 0x102400030 dealloc], RC 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
@interface TCMemoryDebugPrinter : NSObject | |
+ (Class)activateMemoryDebuggingFor:(Class)sourceClass; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "TCMemoryDebugPrinter.h" | |
#import <objc/runtime.h> | |
#import <objc/message.h> | |
#define MEMLOG(classorinstance, instance, sel) NSLog(@"%@[%@ @ %p %@], RC %lu", classorinstance, NSStringFromClass([instance superclass]), instance, NSStringFromSelector(sel), (unsigned long)[instance retainCount]) | |
@interface NSObject () | |
+ (id)fakeAllocWithZone:(NSZone*)zone; | |
@end | |
static dispatch_queue_t proxyMapQueue; | |
static NSMutableDictionary *proxyMap; | |
@implementation TCMemoryDebugPrinter | |
+ (void)initialize | |
{ | |
static dispatch_once_t onceToken; | |
dispatch_once(&onceToken, ^{ | |
proxyMap = [NSMutableDictionary new]; | |
proxyMapQueue = dispatch_queue_create("TCMemoryProxy.proxyMap", DISPATCH_QUEUE_SERIAL); | |
}); | |
} | |
+ (void)setObject:(id)obj forKey:(id)key; | |
{ | |
dispatch_sync(proxyMapQueue, ^{ | |
[proxyMap setObject:obj forKey:key]; | |
}); | |
} | |
+ (id)objectForKey:(id)key; | |
{ | |
__block id ret = nil; | |
dispatch_sync(proxyMapQueue, ^{ | |
ret = [proxyMap objectForKey:key]; | |
}); | |
return ret; | |
} | |
+ (Class)activateMemoryDebuggingFor:(Class)sourceClass; | |
{ | |
NSString *sourceClassName = NSStringFromClass(sourceClass); | |
Class targetClass = [self objectForKey:sourceClassName]; | |
if(!targetClass) { | |
targetClass = [self makeMemorySubclassFor:sourceClass]; | |
[self setObject:targetClass forKey:sourceClassName]; | |
} | |
return targetClass; | |
} | |
+ (Class)makeMemorySubclassFor:(Class)sourceClass; | |
{ | |
NSString *targetClassName = [NSString stringWithFormat:@"__TCMemoryProxy_%@", NSStringFromClass(sourceClass)]; | |
Class targetClass = objc_allocateClassPair(sourceClass, [targetClassName UTF8String], 0); | |
{ | |
SEL sel = sel_registerName("dealloc"); | |
class_addMethod(targetClass, sel, imp_implementationWithBlock(^void(__unsafe_unretained id me) { | |
MEMLOG(@"-", me, sel); | |
void(*superIMP)(id, SEL) = (void*)[sourceClass instanceMethodForSelector:sel]; | |
superIMP(me, sel); | |
}), method_getTypeEncoding(class_getInstanceMethod(sourceClass, sel))); | |
} | |
{ | |
SEL sel = sel_registerName("retain"); | |
class_addMethod(targetClass, sel, imp_implementationWithBlock(^void(__unsafe_unretained id me) { | |
NSLog(@"%@[%@ @ %p %@], RC %lu > %lu", @"-", NSStringFromClass([me superclass]), me, NSStringFromSelector(sel), (unsigned long)[me retainCount], (unsigned long)[me retainCount] + 1); | |
void(*superIMP)(id, SEL) = (void*)[sourceClass instanceMethodForSelector:sel]; | |
superIMP(me, sel); | |
}), method_getTypeEncoding(class_getInstanceMethod(sourceClass, sel))); | |
} | |
{ | |
SEL sel = sel_registerName("release"); | |
class_addMethod(targetClass, sel, imp_implementationWithBlock(^void(__unsafe_unretained id me) { | |
NSLog(@"%@[%@ @ %p %@], RC %lu > %lu", @"-", NSStringFromClass([me superclass]), me, NSStringFromSelector(sel), (unsigned long)[me retainCount], (unsigned long)[me retainCount] - 1); | |
void(*superIMP)(id, SEL) = (void*)[sourceClass instanceMethodForSelector:sel]; | |
superIMP(me, sel); | |
}), method_getTypeEncoding(class_getInstanceMethod(sourceClass, sel))); | |
} | |
{ | |
SEL sel = sel_registerName("autorelease"); | |
class_addMethod(targetClass, sel, imp_implementationWithBlock(^id(__unsafe_unretained id me) { | |
NSLog(@"%@[%@ @ %p %@], RC %lu > %lu later", @"-", NSStringFromClass([me superclass]), me, NSStringFromSelector(sel), (unsigned long)[me retainCount], (unsigned long)[me retainCount] - 1); | |
id(*superIMP)(id, SEL) = (void*)[sourceClass instanceMethodForSelector:sel]; | |
id ret = superIMP(me, sel); | |
return ret; | |
}), method_getTypeEncoding(class_getInstanceMethod(sourceClass, sel))); | |
} | |
unsigned int methodCount = 0; | |
Method *methods = class_copyMethodList(sourceClass, &methodCount); | |
for(int i = 0; i < methodCount; i++) { | |
Method method = methods[i]; | |
if([NSStringFromSelector(method_getName(method)) hasPrefix:@"init"]) { | |
NSLog(@"Todo: overload %@", NSStringFromSelector(method_getName(method))); | |
} | |
} | |
objc_registerClassPair(targetClass); | |
SEL origSel = sel_registerName("allocWithZone:"); | |
SEL altSel = sel_registerName("fakeAllocWithZone:"); | |
Class sourceMetaClass = object_getClass(sourceClass); | |
class_addMethod(sourceMetaClass, altSel, imp_implementationWithBlock(^id(__unsafe_unretained id me, NSZone *zone) { | |
NSLog(@"+[%@ @ %p %@]", NSStringFromClass(me), me, NSStringFromSelector(origSel)); | |
id ret = [sourceClass fakeAllocWithZone:zone]; | |
NSLog(@"+[%@ alloc] yields %@", sourceMetaClass, ret); | |
object_setClass(ret, targetClass); | |
return ret; | |
}), method_getTypeEncoding(class_getClassMethod(sourceClass, origSel))); | |
Method origMethod = class_getClassMethod(sourceClass, origSel); | |
class_addMethod(sourceMetaClass, origSel, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); | |
method_exchangeImplementations(class_getInstanceMethod(sourceMetaClass, origSel), class_getInstanceMethod(sourceMetaClass, altSel)); | |
return targetClass; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import "TCMemoryDebugPrinter.h" | |
@interface Base : NSObject | |
- (id)initWithStuff:(id)stuff; | |
@end | |
@interface Child : Base | |
- (id)initWithThing:(id)thing; | |
@end | |
@implementation Child | |
- (id)initWithThing:(id)thing | |
{ | |
return [super initWithStuff:thing]; | |
} | |
- (void)dealloc | |
{ | |
} | |
+ (id)allocWithZone:(NSZone*)z | |
{ | |
return [super allocWithZone:z]; | |
} | |
@end | |
@implementation Base { | |
id _stuff; | |
} | |
- (id)initWithStuff:(id)stuff; | |
{ | |
if(!(self = [super init])) | |
return nil; | |
_stuff = stuff; | |
return self; | |
} | |
- (void)dealloc | |
{ | |
_stuff = nil; | |
} | |
@end | |
int main(int argc, const char * argv[]) | |
{ | |
@autoreleasepool { | |
[TCMemoryDebugPrinter activateMemoryDebuggingFor:[Child class]]; | |
id child = [[Child alloc] initWithThing:@"foo"]; | |
NSLog(@"Hello, World! %@", child); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment