最も簡単な方法は、おそらくメソッドを動的に追加することです。
2番目のステップについて詳しく説明します。
タイプごとに、次のようなメソッドを追加します
-(int)getEngineInt {
return (int()(id,SEL))(objc_msgSend)(engine, _cmd);
}
構造体には objc_msgSend_stret が必要で、float/double にはobjc_msgSend_fpretが必要な場合があることに注意してください (i386 でのみ必要だと思いますが、AMD64 についてはわかりません)。シミュレーターとデバイスの両方をサポートする簡単なハックは次のようなものです (GCC が使用するマクロ名を忘れてしまいました...)
#if __i386
#define objc_msgSend_fpret objc_msgSend
#endif
を実装する+resolveInstanceMethod:
には、事前に転送先のクラスを知る必要があります。エンジンだとしましょう。
+(BOOL)instancesRespondToSelector:(SEL)name
{
return [Engine instancesRespondToSelector:name];
}
+(BOOL)resolveInstanceMethod:(SEL)name
{
// Do we want to super-call first or last? Who knows...
if ([super resolveInstanceMethod:name]) { return YES; }
// Find the return type, which determines the "template" IMP we call.
const char * returntype = [Engine instanceMethodSignatureForSelector:name].methodReturnType;
if (!returnType) { return NO; }
// Get the selector corresponding to the "template" by comparing return types...
SEL template = NULL;
if (0 == strcmp(returntype,@encode(int))
{
sel = @selector(getEngineInt);
}
else if (0 == strcmp(Returntype,@encode(float))
{
...
}
if (!sel) { return NO; }
Method m = class_getInstanceMethod(self,template);
return class_addMethod(self, name, method_getImplementation(m), method_getTypeEncoding(m));
}
または、少し文書化されていないメソッド-forwardingTargetForSelector:があり、これは必要に応じて十分に高速である可能性があります。
編集:または、プロパティ/メソッドを動的にループすることもできます。カテゴリをイントロスペクトする明白な方法はないようですが、それらをprotocolで定義し、次のようなことを行ってから、 protocol_copyMethodDescriptionList()/protocol_copyPropertyList() を@interface Engine:NSObject<Engine> ... @interface Car(DynamicEngine)<Engine>
使用してメソッドを取得し、ゲッターを追加できます。objc_getProtocol("Engine")
プロパティが「メソッド記述リスト」に追加されているかどうかはわかりません。また、「コピー」関数はスーパークラスからメソッド/プロパティをコピーしないことに注意してください。これは(この場合)必要なものです。