Skip to content

Instantly share code, notes, and snippets.

@nevyn
Created August 14, 2012 18:28
Show Gist options
  • Save nevyn/3351509 to your computer and use it in GitHub Desktop.
Save nevyn/3351509 to your computer and use it in GitHub Desktop.
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
#import <Foundation/Foundation.h>
@interface TCMemoryDebugPrinter : NSObject
+ (Class)activateMemoryDebuggingFor:(Class)sourceClass;
@end
#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
#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