1

ForwardInvocation は存在しますが、遅く、コンパイラの警告という厄介な問題があります。マクロを使用して、別のオブジェクトから問題のプロパティを取得する一連のゲッター メソッドをすばやく実装する方法はありますか?

たとえば、Car オブジェクトがある場合、次の実装が必要になる場合があります。

Car.h:

@class SparkPlug;
@class Engine;

. . .

-(int) nPistons;
-(float) horsepower;
-(SparkPlug*) sparkPlug;

Car.m:

. . .

-(int) nPistons {
    return self.engine.nPistons;
}

-(float) horsepower {
    return self.engine.horsepower;
}

-(SparkPlug*) sparkPlug {
    return self.engine.sparkPlug;
}

質問 -- いくつかのマクロをセットアップして、どこかを変更することで、別のメソッドをヘッダー ファイルと実装ファイルの両方に追加できるようにすることはできますか?

例 MagicForwardingMacro (nPistons, int, engine);

理想的には、後で同様の戦略を使用して Person の firstName、lastName、placeOfBirth、および dateOfBirth プロパティを彼または彼女のbirthCertificate から取得したい場合に、マクロを再利用できるようにする方法です。

4

3 に答える 3

1

最も簡単な方法は、おそらくメソッドを動的に追加することです。

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")プロパティが「メソッド記述リスト」に追加されているかどうかはわかりません。また、「コピー」関数はスーパークラスからメソッド/プロパティをコピーしないことに注意してください。これは(この場合)必要なものです。

于 2011-06-28T02:38:45.550 に答える
0

残念ながら、プロパティ宣言で転送の種類を指定することはできないと思うので、Objective-C2.0プロパティが機能するとは思いません。

2つの異なる場所にテキストを挿入する1つのマクロを持つことはできません。ただし、次のように2つのマクロを使用できます。

//This could also take the third argument and discard it, if you like
#define FORWARDI(type, prop) - (type)prop;
#define FORWARDM(type, prop, owner) - (type)prop { return owner.prop; }

//In the header...
FORWARDI(float, nPistons)

//In the implementation...
FORWARDM(float, nPistons, self.engine)

メソッドがヘッダーファイルに表示されなくてもかまわない場合(たとえば、クラスの実装自体の中でこれらのメソッドのみを使用する場合)、実装ファイルマクロを単独で使用することもできます。

これは所有者のタイプに依存しませんが、どの式でも機能するはずです。

于 2011-06-28T01:54:43.173 に答える
0

欲しいものに近づいています。いくつかのしつこい詳細が残っています:

ForwardingInclude.h:

// no include guard; we want to be able to include this multiple times
#undef forward
#ifdef IMPLEMENTATION
#define forward(a, b, c)  -(a) b { return [[self c] b]; }
#else
#define forward(a, b, c)  -(a) b;
#endif

CarForwarding.h:

// again, no include guard
#include ForwardingInclude.h
forward(int, nPistons, engine)
forward(SparkPlug* sparkPlug, engine)

Car.h:

@interface Car: SomeSuperclass {
  // some ivars
}

. . .

#include CarForwarding.h

Car.m:

. . .

@implementation Car

#define IMPLEMENTATION
#include CarForwarding.h

しつこい詳細:

1)その#defineIMPLEMENTATION行が好きではありません。CarForwarding.hに、現在実装に含まれているかどうかを何らかの方法で自動的に検出してもらいたいです。

2)転送ファイルで定義されたものを、ヘッダーに人間が読める形式で表示することができれば、すばらしいでしょう。または、さらに良いことに、「転送」定義をCar.hファイルに直接書き込むので、CarForwarding.hファイルはまったく必要ありません。

于 2011-06-28T03:50:44.353 に答える