objc/runtime.hをフル盛りで使うとこんなことができる。
- (void)testDynamicClassDefinition
{
// コンパイル時に存在しないクラスをallocateする
Class dynamicClass = objc_allocateClassPair([NSObject class], "DynamicClass", 0);
// コンパイル時に存在しないクラスに存在しないメソッドを実装する
SEL dynamicSelector = NSSelectorFromString(@"whoami");
IMP dynamicImp = imp_implementationWithBlock(^{return @"I'm DynamicClass!!";});
BOOL added = class_addMethod(dynamicClass, dynamicSelector, dynamicImp, @encode(NSString*));
XCTAssert(added, @"メソッドが追加されている");
// コンパイル時に存在しないクラスを新規にruntimeに登録する
objc_registerClassPair(dynamicClass);
// コンパイル時に存在しないクラスをインスタンス化する
typeof(dynamicClass) dynamicInstance = objc_msgSend(dynamicClass, @selector(new));
XCTAssert(dynamicInstance, @"ちゃんとインスタンス化されている");
// コンパイル時に存在しないインスタンスメソッドを呼び出す
NSString *s = nil;
XCTAssertNoThrow(s = objc_msgSend(dynamicInstance, dynamicSelector), @"メソッドが呼び出せる");
XCTAssert([s isEqualToString:@"I'm DynamicClass!!"], @"返り値もただしい");
}
キモイよぉ……