8

ユーザーがクラスの特定のインスタンスからメソッドを呼び出して実行時に解決できる動的コードを作成しようとしています。情報を取得するための実装は存在しますが、インスタンスごとであるため、情報にアクセスするためのメソッドはありません。

たとえば、ユーザーは、クラスに存在しない「getSomething」という名前のメソッドを呼び出したい場合があります。

[someInstance getSomething]

この状況では、作業中のインスタンスにのみ適用される変数の戻り値の型を持つ実装を解決したいと考えています。Objective-C のclass_addMethodを使用することを検討していましたが、その動作について 100% 確信が持てません。ドキュメントでは、これを使用してクラスまたはインスタンスメソッドを追加できると主張しています。このクラスを呼び出すと、特定のインスタンスのみにメソッドが追加されますか?それとも、後で作成されるすべてのインスタンスにメソッドが追加されるように、クラスにメソッドが追加されますか? また、メソッドが追加されると削除できないことも読みました。

おそらく私のアプローチは正しくないので、代替案があれば教えていただければ幸いです。すでに実装されているセレクターを理解するクラスがないため、メッセージ転送を使用できません。

4

3 に答える 3

8

これを行う別の方法は、動的サブクラスを使用することです。

- (void)addCustomMethodToObject:(id)object {
  Class objectClass = object_getClass(object);
  SEL selectorToOverride = ...; // this is the method name you want to override

  NSString *newClassName = [NSString stringWithFormat:@"Custom_%@", NSStringFromClass(objectClass)];
  Class c = NSClassFromString(newClassName);
  if (c == nil) {
    // this class doesn't exist; create it
    // allocate a new class
    c = objc_allocateClassPair(objectClass, [newClassName UTF8String], 0);
    // get the info on the method we're going to override
    Method m = class_getInstanceMethod(objectClass, selectorToOverride);
    // add the method to the new class
    class_addMethod(c, selectorToOverride, (IMP)myCustomFunction, method_getTypeEncoding(m));
    // register the new class with the runtime
    objc_registerClassPair(c);
  }
  // change the class of the object
  object_setClass(object, c);
}

id myCustomFunction(id self, SEL _cmd, [other params...]) {
  // this is the body of the instance-specific method
  // you may call super to invoke the original implementation
}

これを行った後object、オーバーライドされたメソッドのみが受信されます。これは、特別なクラスのインスタンスである唯一のものになるためです。また、このコードはインスタンス メソッドのみをオーバーライドしますが、クラス メソッドをオーバーライドするように変更することは難しくありません。

いつものように、通常の警告:

  1. 警告実装者: このコードはブラウザで入力されました
  2. 警告オブザーバー: このコードは、キー値の監視ではうまく機能しません
  3. 注意 Threader : このコードはあまりスレッドセーフに見えません
  4. 注意 ARC'er : objc_allocateClassPair()ARC でコンパイルすることはできません。
  5. 警告 開発者: オブジェクトのクラスをいじるのは危険なことです。この種のブードゥー教には完全に正当な用途がありますが、非常にまれです。これを行う必要があると思う場合は、おそらく間違っているので、ここに新しい質問を投稿して、「これは私がしなければならないと思うことですよね?」と言ってください。
于 2011-11-26T16:49:17.593 に答える
2

class_addMethod()インスタンスメソッドをクラスオブジェクトに追加するか、クラスメソッドをメタクラスオブジェクトに追加します。つまり、クラスの1つのインスタンスだけにメソッドを追加することはできません。

代わりに、この動作が本当に必要な場合は、-forwardInvocation:を実装できます。この場合、受信オブジェクトは、メッセージを満たすのに十分な情報があるかどうかを判断できます。-forwardInvocation:の実装には通常、-methodSignatureForSelector:および-respondsToSelector:も実装する必要があることに注意してください。

于 2011-11-26T11:57:00.760 に答える
0

私はclass_addMethodに精通していませんが、これはあなたを明確にするのに役立つかもしれません:

Objective-C では「メソッドを呼び出す」のではなく、実際にメッセージを送信していることを思い出してください。したがって、インスタンス化されたオブジェクトに対して [anyObject anyMethodName] を実行しても安全です。このオブジェクトは、メッセージに応答する場合と応答しない場合があります。

[anyObject RespondsToSelector:@selector(@"anyMethodName")] チェックを使用して、オブジェクトが応答するかどうかを確認できます。それが YES の場合は、[anyObject anyMethodName] 呼び出しを実行します。問題の説明を完全には理解できませんが、呼び出しに応答する場合と応答しない場合があるオブジェクトでいっぱいの異種コンテナーがあるようです。コンテナ内の各オブジェクトでこの「respondsToSelector:」チェックを行うことは、Objective-C ではまったく正常なことであり、優れた設計のように思えます。

すべてのオブジェクトが異なるタイプのデータを返す場合、「id」ジェネリック タイプを使用して処理できます。つまり、id returnData = [anyObject anyMethodName]; 次に、returnData でイントロスペクションを使用するか、[anyObject クラス] によってチェックされた「anyObject」のクラスに基づいて、異なる処理を行うことができます。

以下のようなので、

if([anyObject class] == MyGreatClass) // recast data to MyGreatClassCoolReturnType

これが質問への回答に役立つことを願っています

于 2011-05-06T16:40:46.750 に答える