NSAutorelease#addObject:
以前作った TestPool クラスでは、自分が生成した自動解放プールでなければ autorelease されたオブジェクトを知ることができなかった。そこでランタイムAPI の class_replaceMethod を使って、addObject メソッドの中でログを出力するようにしてみた。
static IMP originalImp; id TEST_addObjectImp(id self, SEL _cmd, id anObject) { NSLog(@"replaced imp was called\n"); NSLog(@"%s#%s\n", object_getClassName(self), sel_getName(_cmd)); NSLog(@"arg = class %s: %@\n", object_getClassName(anObject), anObject); return originalImp(self, _cmd, anObject); } BOOL TEST_replaceMethod(Class replaceClass) { originalImp = class_replaceMethod([NSAutoreleasePool class], @selector(addObject:), (IMP)TEST_addObjectImp, "v@:@"); NSLog(@"replace '%s#addObject:' with 'TEST_addObjectImp'\n", object_getClassName(replaceClass)); return originalImp != NULL; } int main (int argc, const char * argv[]) { NSAutoreleasePool * pool; id anObject; BOOL succ; succ = TEST_replaceMethod([NSAutoreleasePool class]); // --> replace 'NSAutoreleasePool#addObject:' with 'TEST_addObjectImp' pool = [[NSAutoreleasePool alloc] init]; NSLog(@"replace result: %s\n", succ ? "YES" : "NO"); // --> replace result: YES [NSString stringWithUTF8String:"<autorelease>"]; // 交換したメソッドが呼び出されない [[[NSObject alloc] init] autorelease]; // 交換したメソッドが呼び出されない anObject = [[NSObject alloc] init]; [pool addObject:anObject]; // --> replaced imp was called // NSAutoreleasePool#addObject: // arg = class NSObject: <NSObject: 0x1065c0> // addObject:を直接指定するとさすがに呼び出される [pool drain]; return 0; }
いったい何がおこっているのだろうか。
ここで NSAutoreleasePool を導出してサブクラス TestAutoreleasePool をつくって、そのインスタンスを作るとなぜかリプレースした addObject: が呼び出される。
@interface TestAutoreleasePool : NSAutoreleasePool // 何も継承しない @end @implementation TestAutoreleasePool @end ... TEST_replaceMethod([NSAutoreleasePool class]); // --> replace 'NSAutoreleasePool#addObject:' with 'TEST_addObjectImp' pool = [[TestAutoreleasePool alloc] init]; [NSString stringWithUTF8String:"<autorelease>"]; // --> replaced imp was called // --> TESTAutoreleasePool#addObject: // --> arg = class NSCFString(<autorelease>) [[[NSObject alloc] init] autorelease]; // --> replaced imp was called // --> TESTAutoreleasePool#addObject: // --> arg = class NSObject(<NSObject: 0x1065c0>) ...