根据Apple的documentation on Scripting Bridge performance的说法,我们应该努力对SBElementArrays
使用批处理操作,因为Apple事件调用非常昂贵。
...只要有可能,您应该始终使用“批处理”之一
操作”数组方法,而不是枚举数组。这些
方法避免了枚举的低效率,因为它们发送了一个
一个Apple事件,而不是数组中每个项目一个Apple事件。
我将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的子类。第二次尝试
接下来,我尝试了一种更骇人的方法,在该方法中,我为第一个菜单分别创建了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不是OOP而是RPC加简单的关系查询。
在SBElementArray
浮渣的全部之下,您真正拥有的是一个单一对象说明符,它描述了应用程序Apple Event Object Model中“对象”之间的一对多关系,这是一种以编程方式呈现的用户数据的理想化虚拟表示形式用户界面。
该应用程序还定义了各种Apple事件处理程序以对其AEOM执行操作-创建,删除,移动,复制等-想法是您向该应用程序发送请求,例如duplicate (every track whose artist is "Bjork") to (playlist "Icelandic")
,接收处理程序会精确地为您确定如何执行该操作。
这种查询驱动的方法在实践中的工作效果取决于应用程序对AEOM支持的实现程度:通常,底层的Model层将集合实现为有序数组而不是无序Set,并且由于您基本上是在更频繁地执行集合操作在RDBMS中可以看到,当数组元素相对于彼此移动时,存在各种各样的错误排序和其他错误蔓延的机会。但是基本概念并不完善(只是可靠地实施的PITA);遗憾的是,SB的作者似乎认为“关系图对于Cocoa用户而言太难了”(无疑对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,这可能很麻烦,但至少它是大多数人所理解的,最不诚实的事情)。