脚本桥:组合 SBElementArrays
Scripting Bridge: Combining SBElementArrays
根据 Apple 的 documentation on Scripting Bridge performance,我们应该努力在 SBElementArrays
上使用批处理操作,因为 Apple 事件调用很昂贵。
... whenever possible you should always use one of the “batch
operation” array methods instead of enumerating the array. These
methods avoid the inefficiency of enumeration because they send a
single Apple event rather than one Apple event per item in the array.
我将 Scripting Bridge 与系统事件应用程序一起使用,并且能够成功地从菜单中获取菜单项。它比我之前使用的 NSAppleScript 方法快得多。
我想做的是将几个 SBElementArrays 组合在一起,每个 SBElementArrays 都包含来自不同菜单的菜单项。计划是然后 运行 批量操作一次,而不是对每个菜单单独执行一次。
在我看来这不应该那么复杂,尽管显然我在这方面的知识充其量是有限的。不幸的是,我 运行 犯了严重的错误。
第一次尝试
如果我尝试创建一个空的 SBElementArray 元素,然后遍历添加每组菜单项的菜单项,如下所示:
SBElementArray* menuItemCombinedArray = [[SBElementArray alloc] init];
for (SystemEventsMenuBarItem* menu in menuBar.menus) {
menuItemCombinedArray = [[menuItemCombinedArray arrayByAddingObjectsFromArray:menu.menuItems] mutableCopy];
}
NSArray* menuItemNameArray = [menuItemCombinedArray arrayByApplyingSelector:@selector(name)];
我收到一条错误消息说 [SBElementArray init] should never be used
,这有点奇怪,因为 SBElementArray 是 NSMutableArray 的子类。
第二次尝试
接下来我尝试了一种更 hackier 的方法,我为第一个菜单单独创建了 SBElementArray,然后遍历其余菜单并一次添加这些 SBObject,如下所示:
SBElementArray* menus = menuBar.menus;
SystemEventsMenuBar* firstMenu = menus.firstObject;
SBElementArray* menuItemCombinedArray = firstMenu.menuItems;
[menus removeObjectAtIndex:0];
for (SystemEventsMenuBarItem* menu in menus) {
SBElementArray* tempMenuItemsArray = menu.menuItems;
for (int i = 0; i < tempMenuItemsArray.count; i++) {
[menuItemCombinedArray addObject:[tempMenuItemsArray objectAtIndex:i]];
}
}
NSArray* menuItemNameArray = [menuItemCombinedArray arrayByApplyingSelector:@selector(name)];
但现在我得到一个不同的错误:[SBElementArray addObject:]: can't add an object that already exists.'
总结
根据我所读到的搜索结果,听起来一般的 Scripting Bridge,特别是 SBElementArray,有点靠不住。但 Scripting Bridge 对我来说比 NSAppleScript 快得多,更接近我的目标。我想如果我能让这个优化工作,我的状态会很好。
在此先感谢您的帮助!
SBElementArray 是 不是 数组 - 它是大量虚构的 BS 掩盖了 Apple 事件 IPC 不是 不是 [=47] 的简单事实=] OOP 但 RPC 加上简单的关系查询。
在所有这些 SBElementArray
渣滓之下你真正拥有的是一个单一的对象说明符,它描述了应用程序的 Apple 事件对象模型中 'objects' 之间的一对多关系,一个理想化的虚拟在编程用户界面中呈现的用户数据的表示。
该应用程序还定义了各种 Apple 事件处理程序,用于在其 AEOM 上执行操作 - 创建、删除、移动、复制等。 duplicate (every track whose artist is "Bjork") to (playlist "Icelandic")
,接收处理程序会准确计算出如何为您执行该操作。
这种查询驱动的方法在实践中的效果如何取决于应用的 AEOM 支持的实施情况:底层模型层通常将集合实施为有序数组而不是无序集合,并且由于您基本上是在执行集合操作在 RDBMS 中更常见的那种,嗯,当相对于彼此移动数组元素时,有各种各样的机会出现错误排序和其他错误。但基本概念并非不健全(只是可靠实施的 PITA);唉,SB 作者似乎认为 "Relational Graphs Is Too Hard for Cocoa users"(这无疑让 CoreData 用户大吃一惊),因此请尝试将其全部隐藏在一个臭烘烘、无能的 ORM 之下。
因此,尝试将 NS[Mutable]Array
语义应用于您正在做的问题是绝对没有意义的,因为 SBElementArray
s 不是本地(或远程)数组,而是围绕 AEOM 的残废混淆包装器查询。换句话说,要了解为什么你正在做的事情不起作用以及如何做到这一点,你需要了解 AEOM 的实际运作方式,SB 如何对其运作方式撒谎,以及 SB 如何将其谎言转化为 [非常有限] AEOM 行为。
因此,当您应用 -[SBElementArray arrayByApplyingSelector:]
时,它实际上根本没有执行数组迭代;相反,它正在构造一个 |selector name| of |elements| of...
形式的对象说明符,并在 get
事件中将其发送到应用程序以进行解析;结果是指定属性的值列表。当然,当你想执行除简单 get
操作以外的任何操作时,这一切都变得毫无用处,例如set (rating of every track of playlist "Icelandic") to 100
,因为 SB API 太残废和偏见,无法让你表达这一点,即使这是一个完全有效的请求。
...
TL;DR:在 SB 中尝试做任何不重要的事情完全是浪费时间,因为你越努力,它的伪 OO 伪造就越崩溃。 仅 [官方支持] 正确执行 Apple 事件的方法是通过 AppleScript,正如您所说,通过 NSAppleScript
使用 AS 是一种打腹股沟的练习,几乎没有比SB(尽管部分原因是您无疑做错了,即通过字符串混搭生成自定义 AS 源代码并即时编译和执行它,而不是 calling parameterized handlers in precompiled .scpt
files 从您的应用程序包中加载)。
幸运的是,10.6 引入了 AppleScript-ObjC 桥接器,虽然它本身也有一些缺点,但它是迄今为止集成 AS 和 ObjC 代码最简单快捷的方法,因为它允许您定义 AppleScript script objects that appear to your ObjC code almost as if they were native Cocoa classes and instances .这将是我向您推荐的方法,并且除了琐碎的任务之外,别管 SB(或者完全忘记它并坚持使用 AS,这可能是 naff,但至少它被大多数人理解,不那么不诚实)。
根据 Apple 的 documentation on Scripting Bridge performance,我们应该努力在 SBElementArrays
上使用批处理操作,因为 Apple 事件调用很昂贵。
... whenever possible you should always use one of the “batch operation” array methods instead of enumerating the array. These methods avoid the inefficiency of enumeration because they send a single Apple event rather than one Apple event per item in the array.
我将 Scripting Bridge 与系统事件应用程序一起使用,并且能够成功地从菜单中获取菜单项。它比我之前使用的 NSAppleScript 方法快得多。
我想做的是将几个 SBElementArrays 组合在一起,每个 SBElementArrays 都包含来自不同菜单的菜单项。计划是然后 运行 批量操作一次,而不是对每个菜单单独执行一次。
在我看来这不应该那么复杂,尽管显然我在这方面的知识充其量是有限的。不幸的是,我 运行 犯了严重的错误。
第一次尝试
如果我尝试创建一个空的 SBElementArray 元素,然后遍历添加每组菜单项的菜单项,如下所示:
SBElementArray* menuItemCombinedArray = [[SBElementArray alloc] init];
for (SystemEventsMenuBarItem* menu in menuBar.menus) {
menuItemCombinedArray = [[menuItemCombinedArray arrayByAddingObjectsFromArray:menu.menuItems] mutableCopy];
}
NSArray* menuItemNameArray = [menuItemCombinedArray arrayByApplyingSelector:@selector(name)];
我收到一条错误消息说 [SBElementArray init] should never be used
,这有点奇怪,因为 SBElementArray 是 NSMutableArray 的子类。
第二次尝试
接下来我尝试了一种更 hackier 的方法,我为第一个菜单单独创建了 SBElementArray,然后遍历其余菜单并一次添加这些 SBObject,如下所示:
SBElementArray* menus = menuBar.menus;
SystemEventsMenuBar* firstMenu = menus.firstObject;
SBElementArray* menuItemCombinedArray = firstMenu.menuItems;
[menus removeObjectAtIndex:0];
for (SystemEventsMenuBarItem* menu in menus) {
SBElementArray* tempMenuItemsArray = menu.menuItems;
for (int i = 0; i < tempMenuItemsArray.count; i++) {
[menuItemCombinedArray addObject:[tempMenuItemsArray objectAtIndex:i]];
}
}
NSArray* menuItemNameArray = [menuItemCombinedArray arrayByApplyingSelector:@selector(name)];
但现在我得到一个不同的错误:[SBElementArray addObject:]: can't add an object that already exists.'
总结
根据我所读到的搜索结果,听起来一般的 Scripting Bridge,特别是 SBElementArray,有点靠不住。但 Scripting Bridge 对我来说比 NSAppleScript 快得多,更接近我的目标。我想如果我能让这个优化工作,我的状态会很好。
在此先感谢您的帮助!
SBElementArray 是 不是 数组 - 它是大量虚构的 BS 掩盖了 Apple 事件 IPC 不是 不是 [=47] 的简单事实=] OOP 但 RPC 加上简单的关系查询。
在所有这些 SBElementArray
渣滓之下你真正拥有的是一个单一的对象说明符,它描述了应用程序的 Apple 事件对象模型中 'objects' 之间的一对多关系,一个理想化的虚拟在编程用户界面中呈现的用户数据的表示。
该应用程序还定义了各种 Apple 事件处理程序,用于在其 AEOM 上执行操作 - 创建、删除、移动、复制等。 duplicate (every track whose artist is "Bjork") to (playlist "Icelandic")
,接收处理程序会准确计算出如何为您执行该操作。
这种查询驱动的方法在实践中的效果如何取决于应用的 AEOM 支持的实施情况:底层模型层通常将集合实施为有序数组而不是无序集合,并且由于您基本上是在执行集合操作在 RDBMS 中更常见的那种,嗯,当相对于彼此移动数组元素时,有各种各样的机会出现错误排序和其他错误。但基本概念并非不健全(只是可靠实施的 PITA);唉,SB 作者似乎认为 "Relational Graphs Is Too Hard for Cocoa users"(这无疑让 CoreData 用户大吃一惊),因此请尝试将其全部隐藏在一个臭烘烘、无能的 ORM 之下。
因此,尝试将 NS[Mutable]Array
语义应用于您正在做的问题是绝对没有意义的,因为 SBElementArray
s 不是本地(或远程)数组,而是围绕 AEOM 的残废混淆包装器查询。换句话说,要了解为什么你正在做的事情不起作用以及如何做到这一点,你需要了解 AEOM 的实际运作方式,SB 如何对其运作方式撒谎,以及 SB 如何将其谎言转化为 [非常有限] AEOM 行为。
因此,当您应用 -[SBElementArray arrayByApplyingSelector:]
时,它实际上根本没有执行数组迭代;相反,它正在构造一个 |selector name| of |elements| of...
形式的对象说明符,并在 get
事件中将其发送到应用程序以进行解析;结果是指定属性的值列表。当然,当你想执行除简单 get
操作以外的任何操作时,这一切都变得毫无用处,例如set (rating of every track of playlist "Icelandic") to 100
,因为 SB API 太残废和偏见,无法让你表达这一点,即使这是一个完全有效的请求。
...
TL;DR:在 SB 中尝试做任何不重要的事情完全是浪费时间,因为你越努力,它的伪 OO 伪造就越崩溃。 仅 [官方支持] 正确执行 Apple 事件的方法是通过 AppleScript,正如您所说,通过 NSAppleScript
使用 AS 是一种打腹股沟的练习,几乎没有比SB(尽管部分原因是您无疑做错了,即通过字符串混搭生成自定义 AS 源代码并即时编译和执行它,而不是 calling parameterized handlers in precompiled .scpt
files 从您的应用程序包中加载)。
幸运的是,10.6 引入了 AppleScript-ObjC 桥接器,虽然它本身也有一些缺点,但它是迄今为止集成 AS 和 ObjC 代码最简单快捷的方法,因为它允许您定义 AppleScript script objects that appear to your ObjC code almost as if they were native Cocoa classes and instances .这将是我向您推荐的方法,并且除了琐碎的任务之外,别管 SB(或者完全忘记它并坚持使用 AS,这可能是 naff,但至少它被大多数人理解,不那么不诚实)。