4

理想的には、渡されたクラスのメソッドを読み取り、実行時にすべてを単一のセレクターにマップしてから、元のセレクターに転送するように設定されたクラスがあります。

これは現在機能しますが、一度に実行できるのは1つのメソッドのみです。問題は、最初のメソッドをスウィズすると、メソッドをキャッチして転送するIMPが他のメソッドIMPと交換されたことにあるようです。新しく交換されたIMPを使用して他のIMPを置き換えるため、この失敗をさらに試みることはできません。

1)つまり、MethodA、MethodB、およびCustomCatchAllMethodがあります。

2)MethodAをCustomCatchAllMEthodと交換します。MethodA-> CustomCatchAllMethod、CustomCatchAllMethod-> MethodA

3)CustomCatchAllMethodを使用してMethodBにスワップしようとしましたが、CustomCatchAllMethodがMethodAになっているため、MethodBはMethodAおよびMethodA->MethodBになります。

では、インターセプトする新しいセレクターごとに、IMPの新しいインスタンスを取得/コピーするにはどうすればよいですか?

上記のフローの大まかなモックアップは次のとおりです。

void swizzle(Class classImCopying, SEL orig){
 SEL new = @selector(catchAll:);
 Method origMethod = class_getInstanceMethod(classImCopying, orig);
 Method newMethod = class_getInstanceMethod(catchAllClass,new);
 method_exchangeImplementations(origMethod, newMethod);
}

//In some method elsewhere

//I want this to make both methodA and methodB point to catchAll:
swizzle(someClass, @selector(methodA:));
swizzle(someClass, @selector(methodB:));
4

2 に答える 2

3

その一般的なメソッド-スウィズリングパターンは、1つのメソッドを他のメソッドとインターセプトする場合にのみ機能します。あなたの場合、基本的に、実装をcatchAll:どこにでも挿入するのではなく、周りに移動します。

これを適切に行うには、次を使用する必要があります。

IMP imp = method_getImplementation(newMethod);
method_setImplementation(origMethod, imp);

ただし、これには1つの問題があります。それは、元の実装にどのように転送するかということです。
それが元のパターンが使用したものexchangeImplementationsです。

あなたの場合、あなたは次のことができます:

  • 元ののテーブルを保持するIMPまたは
  • 元のメソッドの名前をいくつかの共通のプレフィックスに変更して、からそれらへの呼び出しを作成できるようにしますcatchAll:

同じメソッドを介してすべてを転送する場合にのみ、同じアリティのメソッドを処理できることに注意してください。

于 2012-02-11T18:12:58.327 に答える
0

ブロックを使用して元のIMPをキャプチャし、ブロックのIMPを取得して、メソッドの実装として設定できます。

Method method = class_getInstanceMethod(class, setterSelector);
SEL selector = method_getName(method);
IMP originalImp = method_getImplementation(method);

id(^block)(id self, id arg) = ^id(id self, id arg) {
    return ((id(*)(id, SEL, id))originalImp)(self, selector, arg);
};

IMP newImp = imp_implementationWithBlock(block);
method_setImplementation(method, newImp);
于 2014-02-25T21:28:36.407 に答える