脚本桥:结合SBElementArrays

时间:2015-07-28 15:01:57

标签: objective-c macos cocoa scripting-bridge

根据Apple的documentation on Scripting Bridge performance,我们应该努力在SBElementArrays上使用批处理操作,因为Apple事件调用很昂贵。

  

......只要有可能,你应该总是使用其中一个批次   “数组方法而不是枚举数组。这些   方法避免了枚举的低效率,因为它们发送了一个   单个Apple事件,而不是阵列中每个项目的一个Apple事件。

我正在使用Scripting Bridge和System Events应用程序,我可以成功从菜单中获取菜单项。它比我之前使用的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的子​​类。

第二次尝试

接下来我尝试了一种更黑客的方式,我在第一个菜单中单独创建SBElementArray,然后循环浏览其余菜单并一次添加一个SBObjects,如下所示:

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快得多,更接近我的目标。我想如果我能够进行这种优化工作,我会处于良好的状态。

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:4)

SBElementArray 一个阵列 - 很多烟雾镜头BS掩盖了Apple事件IPC OOP但<的其他简单事实strong> RPC加上简单的关系查询。

你真正拥有的所有SBElementArray渣滓是一个单一的物体说明符,描述了物体之间的一对多关系&#39;在应用程序的Apple事件对象模型中,是在程序化用户界面中呈现的用户数据的理想化虚拟表示。

该应用程序还定义了各种Apple事件处理程序,用于在其AEOM上执行操作 - 创建,删除,移动,复制等 - 这个想法是您向应用程序发送请求,例如duplicate (every track whose artist is "Bjork") to (playlist "Icelandic"),接收处理程序准确地计算出如何为您执行该操作。

这种查询驱动方法在实践中的运作情况取决于应用程序的AEOM支持的实现程度:底层模型层通常将集合实现为有序数组而不是无序集,并且因为您需要基本上执行RDBMS中更常见的类型的集合操作,以及在将数组元素相对于彼此移动时会出现错误排序和其他错误的各种机会。但基本概念并不是不合理的(只是PITA可靠地实施);唉,SB的作者似乎认为&#34;关系图对于Cocoa用户来说太难了#34; (这无疑给CoreData用户带来了巨大的惊喜),所以尽量将它隐藏在一个臭的,无能的ORM之下。

因此,在你正在做的事情中尝试将NS[Mutable]Array语义应用于问题绝对毫无意义,因为SBElementArray不是本地(或远程)数组,而是残缺不全围绕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但至少它是大多数人理解的,不那么不诚实的naff)。 / p>