ScriptingBridge - “幕后”如何工作

Sea*_*ris 4 smalltalk objective-c objective-c-runtime pharo scripting-bridge

上下文:我正在开发Pharo/Smalltalk -> Objective-C 桥

场景:在以下 Objective-C ScriptingBridge 片段中:

iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];

iTunesTrack *currentTrack = iTunes.currentTrack; //[1]
// This low level way works too
//iTunesTrack *currentTrack = [iTunes propertyWithCode: 'pTrk']; //[2]

[iTunes playpause]; //[3]
Run Code Online (Sandbox Code Playgroud)

问题:桥用于class_getInstanceMethod确定对象是否理解消息/选择器,但对于脚本消息,如playpause

问题#1 为什么class_getInstanceMethod像这样的脚本消息会返回 NULL playpause?同样的问题class_copyMethodList?脚本消息有什么特别之处,它们的行为与其他 Obj-C 消息不同(除非它们这样做!)?

问题 #2 [已解决 - 请参阅@Matt 的回答]

根据文档,SB 在“iTunes 应用程序的动态定义子类”中放置了“自动处理 Apple 事件发送的应用程序特定方法”?并且,鉴于class_getInstanceMethod无法找到这种行为(见下文),桥接器测试它的可靠方法是什么(即是否存在这样的方法/消息)?

Objective-C 运行时 API 报告的结果好坏参半。一方面,该类iTunesApplication似乎没有任何方法(或与此相关的属性):

  • class_copyMethodList([iTunes class]...返回零的方法
  • class_getInstanceMethod桥接器用来查找和执行方法的 失败。

另一方面,#playpause可以通过API的其他部分进行查询和发送:

  • respondsToSelector:-> 正确
  • methodSignatureForSelector:返回签名
  • performSelector:实际发送消息

奇怪的是,在 Obj-C 中methodForSelector:@"playpause"成功返回一个IMP,但如果从桥的另一边发送则崩溃。

问题 #3 [已解决]

如何模拟/复制[3]?

@Willeke 在评论中回答:[iTunes sendEvent:'hook' id:'PlPs' parameters:0]

mat*_*att 5

\n

如果 SB 不使用 Objective-C 消息,那么文档中“SBApplication 的子类实现自动处理 Apple 事件发送的应用程序特定方法”是什么意思?为什么 iTunes respondsToSelector: @"playpause" 会返回 true?[iTunes 播放暂停] 是如何工作的?等等等等。

\n
\n\n

它之所以有效,是因为您在脚本桥应用程序中所做的第一件事就是生成标头。在 Catalina 中你可以这样做:

\n\n
sdp -f h --basename iTunes /System/Applications/Music.app/Contents/Resources/com.apple.Music.sdef\n
Run Code Online (Sandbox Code Playgroud)\n\n

这会读取 iTunes 字典 ( sdef) 并生成一组类似 Objective-C 类的标头。现在您有了一个iTunes.h文件,您可以将其包含在应用程序项目中并导入到代码中。它包含这一行:

\n\n
- (void) playpause;  // toggle the playing/paused state of the current track\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,nowplaypause被显式声明为可以发送到 iTunesApplication 对象的合法命令。然后,当您实际运行应用程序时,您会说

\n\n
iTunesApplication* tunes = (iTunesApplication*)[SBApplication applicationWithBundleIdentifier:@"com.apple.music"];\n
Run Code Online (Sandbox Code Playgroud)\n\n

这会导致您的应用程序与 iTunes(音乐)对话并再次sdef获取字典 ( ) ,从而生成标头中声明的方法的实现。该命令的实现正如其所说:即将事件发送到 iTunes。playpausesdefhookPlPs

\n\n

这既解释了为什么你被允许说话 playpause,也解释了当你说话时会发生什么。

\n\n

这就是AppleScript\xe2\x80\x94,它是一个应用程序,提供了您可以使用Apple 事件对其说出的内容的列表,以及引用这些Apple 事件的类似英语的术语。

\n\n

因此,如果想编写一个桥接器,则必须做同样的事情:您需要提供一种方法来搜索目标应用程序的资源,并将该信息转换为以您的sdef语言给出相应命令的方法,无论它是什么语言是。

\n