According to Apple's documentation on Scripting Bridge performance, we should strive to use batch operations on SBElementArrays
, since Apple event calls are expensive.
... 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.
I'm using Scripting Bridge with the System Events application, and I'm able to get menu items from the menus successfully. It's much faster than the NSAppleScript method I was using previously.
What I'd like to do is combine together several SBElementArrays, each of which is holding menu items from different menus. The plan is to then run the batch operation once instead of doing it for each menu individually.
It seems to me like this shouldn't be that complicated, though obviously my knowledge in this area is limited at best. Unfortunately I'm running into serious errors.
First Attempt
If I attempt to create an empty SBElementArray element and then loop through the menu items adding each set of menuitems, like so:
SBElementArray* menuItemCombinedArray = [[SBElementArray alloc] init];
for (SystemEventsMenuBarItem* menu in menuBar.menus) {
menuItemCombinedArray = [[menuItemCombinedArray arrayByAddingObjectsFromArray:menu.menuItems] mutableCopy];
}
NSArray* menuItemNameArray = [menuItemCombinedArray arrayByApplyingSelector:@selector(name)];
I get an error saying that [SBElementArray init] should never be used
, which is a bit odd since SBElementArray is a subclass of NSMutableArray.
Second Attempt
Next I tried a hackier way, where I created the SBElementArray separately for the first menu, then looped through the remaining menus and added those SBObjects one at a time, like so:
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)];
But now I get a different error: [SBElementArray addObject:]: can't add an object that already exists.'
Summary
From what I've read searching around, it sounds like Scripting Bridge in general, and SBElementArray specifically, are kind of wonky. But Scripting Bridge has been much faster for me than NSAppleScript, much closer to what I'm aiming for. I think if I could get this optimization working I'd be in great shape.
Thanks in advance for any help!