3

いくつかのクラスメソッド呼び出しをいくつかの「静的」クラスに転送したいファサードシングルトンがあります。

一見、forwardInvocation:可能な解決策のように見えましたが、は、クラス自体ではなく、インスタンスへのポインタNSInvocationのみinvokeWithTarget:setTarget:受け入れます。idターゲットとして引き渡そうとしましたが、どこか[MyTargetClass class]を呼び出すと「既知のクラスメソッドがありません[...]」というエラーが発生します。[Facade someForwardedMethod]電話をかける[[Facade sharedInstance] someForwardedMethod]と、「表示されない@interface[...]がセレクター[...]を宣言します」というエラーが表示されます。

もちろん、respondsToSelector:とをオーバーライドする必要があることも認識しているmethodSignatureForSelector:ので、コードは次のようになります。

- (BOOL)respondsToSelector:(SEL)aSelector {
    if ([super respondsToSelector:aSelector]) {
        return YES;
    } else {
        return [MyTargetClass respondsToSelector:aSelector];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    NSMethodSignature* signature = [super methodSignatureForSelector:selector];
    if (!signature) {
        signature = [MyTargetClass methodSignatureForSelector:selector];
    }
    return signature;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {

    SEL aSelector = [anInvocation selector];

    if ([MyTargetClass respondsToSelector: aSelector]) {
        [anInvocation invokeWithTarget:[MyTargetClass class]];
    } else {
        [super forwardInvocation:anInvocation];
    }
}

これを機能させる方法はありますか、それとも別のアプローチを選択する必要がありますか?


編集:私はロブ・ネピアが彼の答えで言及している両方の方法を試しました、ここに私の発見があります:

ファサードインスタンスを介してターゲットクラスのクラスメソッドを呼び出すことができます。

[(id)[Facade sharedInstance] doSomethingClassyInTargetClass];

思ったより少し醜いですが、うまくいきます。ただし、ファサードクラスをアドレス指定するときに、ターゲットクラスのクラスメソッドを呼び出すことができません。コンパイラを静かにするために私は書くことができます

[(Class)[Facade class] doSomethingClassyInTargetClass];

ただし、実行時に「NSInvalidArgumentException」「[FacadedoSomethingClassyInTargetClass]:認識されないセレクターがクラス[...]に送信されました」をスローします。どうやらファサードクラスのクラスメソッドは、を尊重せずに解決されるようですforwardInvocation:が、結局のところ、それは-前にあります...

4

1 に答える 1

5

ああ、「目に見えない@interface [...]がセレクターを宣言している[...]」を追加すると、それはすべて明らかです。

インスタンスメソッドだけの単純なフォームを考えてみましょう。を実装する-forwardInvocation:と、次のようなコードが作成されます。

[obj doSomething];

さて、それがそれに応答するとしても、MyObject実際にはそのヘッダーで主張しません。doSomethingしたがって、コンパイラは、これはおそらく機能しないと文句を言います。その修正は次のとおりです。

[(id)obj doSomething];

何かを宣言するidと、コンパイラは、ターゲットが実際にセレクタを実装しているかどうかのチェックを停止します。ただし、インターフェイスで宣言されていない可能性もありますdoSomething。次に、コンパイラは、おそらくタイプミスであると再度​​疑って、警告を出します。また、「インターフェイスなし」とは、「コンパイラがアクセスできるインターフェイスがない」という意味です。コンパイラは、このコンパイル単位のインターフェイス(つまり、.mファイルとそれに含まれるヘッダー)にのみアクセスできます。したがって、このセレクターを定義するヘッダーが含まれていることを確認する必要があります。

クラスの場合、を使用することはできませんが、次のような同じことを達成するためidに使用できるはずです。Class

[(Class)MyClass doSomethingClassy];

iOS:PTL第4章のRNObserverManagerサンプルコードを見たい場合は、同様のことを示しています。

forwardTargetForSelector:また、KenThomasesが参照していることも確認する必要があります。


sharedInstanceにキャストする必要はありませんid。署名を変更するだけです。常にそのように使用したいからです。

+ (id)sharedInstance;
于 2012-06-02T18:50:13.637 に答える