次のクラス階層を想定します。クラスA
は公に宣言されています:
@interface A : NSObject
+ (A)createInstance;
- (void)a;
@end
クラス_B
は のプライベート サブクラスですA
:
@interface _B : A
- (void)a;
- (void)b;
@end
クラス のオブジェクトは、 のインスタンスを作成して返すA
ファクトリ メソッド を使用してのみ作成する必要があると仮定します。createInstance
_B
A
のインスタンスの機能をインスタンスごとに強化したいと考えています。そこで、次のことを達成するために ISA スウィズルを実行することにしました。
@interface ExtA : A
- (void)a;
@end
@implementation ExtA
- (void)a
{
NSLog("ExtA_a");
[super a];
}
@end
そして、NSObject
カテゴリに対して次のメソッドを使用して ISA スウィズリングを行います (ここに示されている単純な実装)。
- (void)changeToSubclass:(Class)cls prefix:(NSString*)prefix suffix:(NSString*)suffix
{
NSString* className = [NSString stringWithFormat:@"%@%@%@", prefix ? prefix : @"", NSStringFromClass(object_getClass(self)), suffix ? suffix : @""];
if([className isEqualToString:NSStringFromClass(object_getClass(self))])
{
className = [NSString stringWithFormat:@"%@(%@)", NSStringFromClass(object_getClass(self)), NSStringFromClass(cls)];
}
Class newSubclass = objc_getClass(className.UTF8String);
if(newSubclass == nil)
{
newSubclass = objc_allocateClassPair(object_getClass(self), className.UTF8String, 0);
objc_registerClassPair(newSubclass);
unsigned int listCount = 0;
Method *list = class_copyMethodList(cls, &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(newSubclass, method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
listCount = 0;
list = class_copyMethodList(objc_getMetaClass(class_getName(cls)), &listCount);
for(int i = 0; i < listCount; i++)
{
class_addMethod(objc_getMetaClass(class_getName(newSubclass)), method_getName(list[i]), method_getImplementation(list[i]), method_getTypeEncoding(list[i]));
}
free(list);
}
object_setClass(self, newSubclass);
}
[super a];
すべてが機能しているように見えますが、期待どおりに動作しないことに気付きました。-[A a]
実行時のスーパークラスが実際に_B
.
への呼び出しを次のコードに置き換えるとsuper
機能しますが、見苦しく、開発者による知識と作業が必要です。
struct objc_super superInfo = {
self,
[self superclass]
};
objc_msgSendSuper(&superInfo, @selector(a));
呼び出し時にコンパイラは何を発行し、super
この発行されたコードを変更する方法はありますか?