1

私は、Cocoa Scripting に関する独自の非 ObjC フレームワークを作成しています (C、C++、Java、または私の場合は Xojo でスクリプト可能な Mac アプリを作成することを考えてください)。

ObjC クラスに実際のメソッドを追加する代わりに、オブジェクト ファーストのメソッド呼び出しをインターセプトできるようにしたいと考えています (フレームワークは、アプリ コードがどのメッセージを処理できるかを事前に認識できないため、できません。代わりに、スクリプト エンジンから受信したコマンド メッセージを受信して​​渡す必要があります)。

たとえば、プロパティのゲッターとセッターは、実装によって傍受できます。

-valueForUndefinedKey:
-setValue:forUndefinedKey:

NSScriptKeyValueCodingプロトコルのすべてのメソッドと同様に。

NSCommandScriptこれらの sdef 要素で指定されたメソッドに送信されたメッセージを傍受する同様の方法を探しています。

<responds-to command="reload">
    <cocoa method="reloadList:"/>
</responds-to>

したがって、reloadList:クラスメソッドに追加して実装する代わりに、そのような呼び出しをすべてキャッチする一般的な方法があるかどうか疑問に思います。

クラスメソッドが

+ (BOOL)resolveInstanceMethod:(SEL)sel

を求めて呼び出されreloadList:ます。しかし、同じメソッドは他の多くの目的でも呼び出されます。そのため、そのようなすべての呼び出しをやみくもにインターセプトすることはしません。なぜなら、それらすべてをJava関数に転送して、必要かどうかを伝えるとパフォーマンスが大幅に低下するからです。たとえば、それを処理します。

さらに転送する前に、このセレクターが NSScriptCommand に関連していることを伝える何かがあることを願っています。

4

2 に答える 2

1

指定されたコマンド処理メソッドにブレークポイントを設定した後、次のスタック トレースが表示されました。

#0  0x00000001000197db in -[SKTRectangle rotate:]
#1  0x00007fff8ee0b7bc in __invoking___ ()
#2  0x00007fff8ee0b612 in -[NSInvocation invoke] ()
#3  0x00007fff8eeab5c6 in -[NSInvocation invokeWithTarget:] ()
#4  0x00007fff8b82cbde in -[NSScriptCommand _sendToRemainingReceivers] ()
#5  0x00007fff8b82cf39 in -[NSScriptCommand executeCommand] ()

これは、NSScriptCommand がカスタマイズ可能な特別な転送メカニズムを使用していないように見えますが、NSInvocation を使用してメソッドを呼び出していることを示しています。

このような呼び出しは、次のようにインターセプトできます。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // Look for signatures with exactly one ":"
    if ([[NSStringFromSelector(aSelector) componentsSeparatedByString:@":"] count] == 2) {
        return [NSMethodSignature signatureWithObjCTypes:"@:@@"];
    } else {
        return [super methodSignatureForSelector:aSelector];
    }
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    id arg; // The first and only argument is expected to be of type NSScriptCommand*
    [anInvocation getArgument:&arg atIndex:2];
    if ([arg isKindOfClass:[NSScriptCommand class]]) {
        NSLog(@"executing the command...");
        // when finished, set the return value (which shall be an NSObject)
        id result = nil;
        [anInvocation setReturnValue:&result];
    } else {
        // oops - we cannot handle this
        [super forwardInvocation:anInvocation];
    }
}

resolveInstanceMethod:この形式のインターセプトは、頻繁に呼び出されることはなく、NSScriptCommand の実行などの特定の目的でのみ呼び出されるため、使用するよりもうまく機能します。

ただし、これに関する問題は、他のコードも NSInvocation を使用して他の目的で同じクラスを呼び出す場合、それらの呼び出しが一致するセレクター署名を使用する場合、上記のコードはそれらの呼び出しをインターセプトして処理しない可能性があることです。予期しない動作につながります。

クラスがスクリプト エンジンによってのみ使用され、他の動作を持たないことがわかっている限り (つまり、これらのクラスが NSObject の直接のサブクラスである場合)、これが発生する理由はありません。したがって、クラスが別の環境へのプロキシとしてのみ機能する私の特別なケースでは、これが実行可能な解決策になる可能性があります。

于 2016-03-28T19:14:46.317 に答える