如何支持应用程序(NSApp)类的AppleScript元素(一对多关系)?

时间:2013-08-17 23:27:43

标签: applescript element one-to-many sdef

这就是我所拥有的:

1)在'Info.plist

中包含适当的“权限”条目
  • 'Scriptable':是
  • '脚本定义文件名':myApp.sdef

2)在类扩展“element”标记中包含元素“element”标记:

`<class-extension extends="application" description="The application and top-level scripting object.">
    <!-- various property tags go here -->

<element type="object item" access="r">
<cocoa key="theseObjects"/>
</element>
</class-extension>`

3)包括元素类标记:

<class name="object item" code="Objs" description="Application 'too many' object collection" plural="object items" inherits="item"> // I don't believe 'inherits' name is critical for AS to work
<cocoa class="ObjectItem"/>                     
</class>

4)包括将“NSApplication”脚本支持转发给其委托的委托方法:

- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key {    
if ([key isEqualToString:@"theseObjects"]) {
    return YES;
}
return NO;

}

5)创建一个'ObjectItem'类并将对象说明符放在那里:

 - (NSScriptObjectSpecifier *)objectSpecifier { 
NSScriptObjectSpecifier *containerRef = nil;
NSScriptObjectSpecifier *specifier = [[NSNameSpecifier alloc] initWithContainerClassDescription:[NSScriptClassDescription classDescriptionForClass:[NSApp class]] containerSpecifier:containerRef key:@"theseObjects" name:@"objectName"];
return [specifier autorelease];

6)在应用程序的委托中发布KVO访问器方法:

 - (NSArray *)theseObjects;
   {
ObjectItem *thisObject = [[ObjectItem new] autorelease];
NSArray *thisArray = [NSArray arrayWithObject:thisObject];
return thisArray;
    }
}

7)创建一个AppleScript,它从我的元素getter方法返回对象:

    tell application "SpellAnalysis"
    get theseObjects
    end tell

8)结果: 错误“未定义变量对象。”数字-2753来自“对象”

9)拔掉我的头发

2 个答案:

答案 0 :(得分:3)

关于最初的一对多帖子,作者做了一个很好的工作,概述了脚本几乎的工作步骤。幸运的是,只需要进行一些小的修改就可以解决问题:

1)样本AppleScript

tell application "SpellAnalysis" to get theseObjects 

不可能奏效。 AS不能引用Objective-C变量名称( theseObjects )。 由于 sdef 定义了“对象项”的类和元素类型,因此必须在脚本中使用:

tell application "SpellAnalysis" to get object items -- FIX #1

2)ObjectItem类中的对象说明符应返回当前对象的实际名称属性值,而不是字符串@“objectName”。所以它应该是:

- (NSScriptObjectSpecifier *)objectSpecifier 
{ 
    NSScriptObjectSpecifier *containerRef = nil;
    NSScriptObjectSpecifier *specifier = [[NSNameSpecifier alloc] 
        initWithContainerClassDescription:[NSScriptClassDescription classDescriptionForClass:[NSApp class]] 
        containerSpecifier:containerRef 
        key:@"theseObjects" 
        name:self.name]; // FIX #2

    return specifier;
}

3)支持上述内容的完整且正确的 sdef 是:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary xmlns:xi="http://www.w3.org/2003/XInclude">
    <xi:include href="file:///System/Library/ScriptingDefinitions/CocoaStandard.sdef" xpointer="xpointer(/dictionary/suite)"/>

    <suite name="SpellAnalysis Suite" code="sSIA" description="ToDo">
        <class name="object item" code="Objs" description="ToDo" plural="object items">
            <cocoa class="ObjectItem"/> <!-- KVC: Objective-C class ObjectItem : NSObject -->
            <property name="name" code="pnam" type="text" access="r" description="Its name.">
                <cocoa key="name"/>
            </property>
        </class>
        <class-extension name="SpellAnalysis application" extends="application" description="ToDo">
            <element type="object item" access="r">
                <cocoa key="theseObjects"/> <!-- KVC: app delegate's @property NSArray* theseObjects; -->
            </element>
        </class-extension>
    </suite>
</dictionary>

4)支持上述的ObjectItem.h接口:

@interface ObjectItem : NSObject
    @property (nonatomic, strong) NSString *name;

    - (instancetype)initWithName:(NSString *)name;
@end

5)支持上述的ObjectItem.m文件:

#import "ObjectItem.h"
@implementation ObjectItem

- (instancetype)initWithName:(NSString *)name
{
    if (self = [super init])
    {
        self.name = name;
    }
    return self;
}

- (NSScriptObjectSpecifier *)objectSpecifier 
{ 
    NSScriptObjectSpecifier *containerRef = nil;
    NSScriptObjectSpecifier *specifier = [[NSNameSpecifier alloc] 
        initWithContainerClassDescription:[NSScriptClassDescription classDescriptionForClass:[NSApp class]] 
        containerSpecifier:containerRef 
        key:@"theseObjects" 
        name:self.name]; // FIX #2

    return specifier;
}
@end

6)最后,App委托类中的代码支持上述内容:

#import "MyAppDelegate.h"
#import "ObjectItem.h"

@interface MyAppDelegate ()
@property (nonatomic, strong) NSArray *theseObjects;
@end

@implementation MyAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    self.theseObjects = @[
        [[ObjectItem alloc] initWithName:@"Item A"],
        [[ObjectItem alloc] initWithName:@"Item B"],
        [[ObjectItem alloc] initWithName:@"Item C"]
    ];
}

- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key
{
    if ([key isEqualToString:@"theseObjects"])
    {
        return YES;
    }
    return NO;
}
@end

就是这样,只需修复几个简单错误(一个在脚本中,一个在对象说明符代码中),以下脚本返回预期结果:

tell application "SpellAnalysis"
    get object items -- expect a list of object references for all objects in array
end tell

--> {object item "Item A" of application "SpellAnalysis", object item "Item B" of application "SpellAnalysis", object item "Item C" of application "SpellAnalysis"}

其他脚本也有效:

tell application "SpellAnalysis"
    get object item 2 -- expect a object reference to the second item in the array
end tell

--> object item "Item B" of application "SpellAnalysis"


tell application "SpellAnalysis"
    name of object item 2 -- Expected result: the name property of 2nd obj in array
end tell

--> "Item B"

答案 1 :(得分:0)

我认为我应该发布关于Cocoa Scriptability支持核心数据应用程序的一些附加信息,因为那里的信息很少。我花了至少一个月的时间试图了解如何与这种机制达成协议。

虽然我能够使用索引说明符为我的核心数据应用程序提供AppleScript支持,但我发现使用'uniqueID'说明符提供了更好的选择。更好,因为核心数据到多个关系由无序集支持,而不是数组。核心数据为您提供了一种方法,您可以通过可由Cocoa Scriptability方法评估的对象ID来指定托管对象。然而,要使用uniqueID说明符实现对“太多”关系的支持,需要额外的代码元素。

1)在'sdef'中为您将支持的每个实体提供一个property元素。例如,为了支持我的“级别”实体,我在'levels'类标记中发布以下内容:

<property name="id" code="ID  " type="text" access="r" description="The level's unique id. This may be a temporary id for newly-    created level objects, until they are saved.">
    <cocoa key="uniqueID"/>
</property>

请注意,类型被指定为“text”而不是“说明符”。 AppleEvent机制和Cocoa AppleScript支持之间的“uniqueID”交换将是一个字符串值。另请注意,'cocoa key'值为'uniqueID'。 AppleScript脚本支持支持使用此密钥('sdef'中典型的此类密钥)来识别应用程序中符合KVC模式的方法名称。

2)在包含目标对象的类中发布'valueInWithUniqueID'方法。在此方法中,您提供了一种方法来提取与传递给方法的“uniqueID”相对应的托管对象。这是我的'levelsArray'KVO方法在我的容器类中发布的'Levels'。

- (id)valueInLevelsArrayWithUniqueID:(NSString *)uniqueID;
{
    NSManagedObject *managedObject= [[[NSApp delegate] myManagedObjectContext] objectWithID:[[[NSApp delegate] lessonsManager]    managedObjectIDForURIRepresentation:[NSURL URLWithString:uniqueID]]];
    return managedObject;
}

这是包含'单元'类的'sdef'属性声明:

<property name="id" code="ID  " type="text" access="r" description="The unit's unique id.  This may be a temporary id for newly-created unit objects, until they are saved.">
    <cocoa key="uniqueID"/>
</property>

我的'Units'类也唤起了value方法。请注意基本的KVC名称模式:

- (id)valueInUnitsArrayWithUniqueID:(NSString *)uniqueID;
{
NSManagedObject *managedObject= [[[NSApp delegate] lessonsDBase] objectWithID:[[[NSApp     delegate] lessonsManager] managedObjectIDForURIRepresentation:[NSURL   URLWithString:uniqueID]]];

    return managedObject;
}

有了这些,如果我的AppleScript需要从'uniqueID'指定'level'对象,它将被回答。当您拥有实体类的层次结构并且编写AppleScript时,这似乎会发挥作用,1)唤起一个返回对其结果的引用的命令,并且2)使用另一个命令对此返回的结果进行操作:

count (make new section at unit 1 of level 1)

奇怪的是,以下内容没有引出价值方法:

count (unit 1 of level 1)

请注意,它缺少条件1 - 缺少主要命令(例如“make”)。在这种情况下,隐含的AppleScript“get”命令被引发,但Cocoa脚本可视性似乎通过另一种方式确定实体层次结构的值。

3)为从您支持的命令返回的所有对象提供“uniqueID”对象说明符,并为支持其隐含的“get”命令的每个实体子类显式命名为“objectSpecifier”。例如,我在其'performDefaultImplementation'中发布的'clone'命令提供了以下方法:

 NSUniqueIDSpecifier *uniqueIDSpecifier = [[[NSUniqueIDSpecifier allocWithZone:[self zone]]  initWithContainerClassDescription:[NSScriptClassDescription classDescriptionForClass:[NSApp class]] containerSpecifier:sourceContainerSpecifier key:sourceKey uniqueID:uniqueID] autorelease];

根据您是使用命令操作单个对象还是一系列对象,可以直接返回“uniqueIDSpecifier”或将其添加到上次添加后返回的连续此类说明符的数组中。要支持隐含的“get”命令,请在每个托管对象实体子类中发布“uniqueID”对象说明符。以下说明符支持我的'Unit'类子类:

- (NSScriptObjectSpecifier *)objectSpecifier {
    NSScriptObjectSpecifier *containerRef = [[NSApp delegate]levelsSpecifier];
    NSString *uniqueID = [[[self objectID] URIRepresentation] absoluteString]; // This is            the key method for determining the object's 'uniqueID'
if (uniqueID) {
    NSScriptObjectSpecifier *uniqueIDSpecifier = [[[NSUniqueIDSpecifier allocWithZone:   [self zone]]   initWithContainerClassDescription:[containerRef keyClassDescription]  containerSpecifier:containerRef key:@"unitsArray" uniqueID:uniqueID] autorelease];
   [[NSApp delegate] setUnitsSpecifier:uniqueIDSpecifier]; // Post specifier so Units  class specifier can access it
    return uniqueIDSpecifier;
   } else {
      return nil;
   }

请注意,第二个注释行表示我将此说明符的结果发布到全局变量中,以便包含的类的对象说明符可以将此说明符结果用作其容器说明符。应用程序委托是一个地方,所有实体子类都可以访问应用程序范围的访问器和方法,例如此对象说明符结果。

我希望上个月能帮到像我这样的人。