poseAsClass and method_exchangeImplementations
昨日の続き。クラスのポージングを使ってみた。
@interface TESTAutoreleasePool : NSAutoreleasePool @end @implementation TESTAutoreleasePool +(void)load { if (self != [TESTAutoreleasePool class]) return; NSLog(@"%s pose as NSAutoreleasePool\n", class_getName(self)); [self poseAsClass:[NSAutoreleasePool class]]; } -(void)addObject:(id)anObject { NSLog(@"posing method was called\n"); NSLog(@"method = %s#%s\n", object_getClassName(self), sel_getName(_cmd)); NSLog(@"arg = class %s (%@)\n", object_getClassName(anObject), anObject); [super addObject:anObject]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool *pool; id anObject; // --> TESTAutoreleasePool pose as NSAutoreleasePool pool = [[NSAutoreleasePool alloc] init]; [NSString stringWithUTF8String:"<autorelease>"]; // 何も表示されない [[[NSObject alloc] init] autorelease]; // 何も表示されない anObject = [[NSObject alloc] init]; [pool addObject:anObject]; // --> posing method was called // method = NSAutoreleasePool#addObject: // arg = class NSObject(<NSObject: 0x1068f0>) [pool drain]; return 0; }
残念ながらポージングでも NSAutoreleasePool#addObject: を乗っ取ることはできなかった。
しかしポージングは Objective-C では Deprecated である。そのかわり method_exchangeImplementations を使うことができる。
@interface NSAutoreleasePool (TESTAutoreleasePool) -(void)TEST_addObject:(id)anObject; @end @implementation NSAutoreleasePool (TESTAutoreleasePool) +(void)load { if (self == [NSAutoreleasePool class]) { Method originalMethod = class_getInstanceMethod(self, @selector(addObject:)); Method replaceMethod = class_getInstanceMethod(self, @selector(TEST_addObject:)); method_exchangeImplementations(originalMethod, replaceMethod); NSLog(@"method %s was exchanged with %s\n", method_getName(originalMethod), method_getName(replaceMethod)); } else { NSLog(@"load %s\n", class_getName(self)); } } -(void)TEST_addObject:(id)anObject { NSLog(@"exchanged method was called\n"); NSLog(@"method = %s#%s\n", object_getClassName(self), sel_getName(_cmd)); NSLog(@"arg = class %s(%@)\n", object_getClassName(anObject), anObject); [self TEST_addObject:anObject]; } @end int main (int argc, const char * argv[]) { NSAutoreleasePool *pool; id anObject; // --> method addObject: was exchanged with TEST_addObject: pool = [[NSAutoreleasePool alloc] init]; [NSString stringWithUTF8String:"<autorelease>"]; // 何も表示されない [[[NSObject alloc] init] autorelease]; // 何も表示されない anObject = [[NSObject alloc] init]; [pool addObject:anObject]; // --> exchanged method was called // TestAutoreleasePool[2008:10b] method = NSAutoreleasePool#addObject: // arg = class NSObject(<NSObject: 0x106620>) [pool drain]; return 0; }
これでも addObject: を乗っ取れないかという感じである。想像するに NSObject の autorelease メソッドで何かやっているのではないか。自動解放プールのインスタンスが NSAutoreleasePool かそのサブクラスかによって、addObject: でメッセージを送るか独自の仕組みを使うか。さすがにそれは検証できないなあ。