1

我正在围绕 Cocoa Scripting 编写自己的非 ObjC 框架(想想用 C、C++ 或 Java 编写一个可编写脚本的 Mac 应用程序,或者在我的例子中是 Xojo)。

我希望能够拦截任何对象优先的方法调用,而不必将实际方法添加到 ObjC 类(我不能,因为框架不知道应用程序代码可以提前处理哪些消息 - 所以它'相反,一旦命令消息从脚本引擎进入,则必须接收并传递它们)。

例如,任何属性的 getter 和 setter 都可以通过实现来拦截

-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 回答
0

如果它不是基于 Cocoa 的应用程序,那么您可能最好忘记使用 Cocoa 脚本,因为它与 Cocoa 架构的其余部分高度耦合,直接使用安装您自己的 AE 处理程序NSAppleEventManager并在它们之间编写您自己的 View-Controller 胶水无论您最终在其中实现模型。另请参阅:Mac Carbon 应用程序中的 Scriptability (AppleScript)

ETA:想想看,你可能想在网上翻找一下,看看你是否可以挖掘出任何旧的 C++ AEOM 框架,因为 ISTR 在 OS X 之前的日子里有一两个。可能需要一些更新,并且可能会或可能不会有任何好处(但是 CS 也相当糟糕),但它比完全从头开始设计和实现一个好的、健壮的、惯用的(甚至简化的)要容易得多AEOM 框架是一个巨大的 PITA,即使您确实知道自己在做什么(几乎没有人知道)。

于 2016-03-28T17:21:07.880 回答