Реализация действия Objective C

Определенное преимущество действий, записанных в Objective C (по сравнению с действиями AppleScript), состоит в том, что у них есть больший доступ к диапазону системных ресурсов OS X. Действия, использующие код Objective C, могут включить функции не только платформ Objective C, такие как Основа и Набор Приложения, но фактически любой другой платформы (Поскольку Objective C является надмножеством ANSI C, класс Objective C может вызвать функции, опубликованные в интерфейсе C). Просто действия Objective C имеют недостаток возмещения: они не могут легко приложения управления или получать доступ к их функциям за исключением тех приложений, предлагающих общедоступный API.

В целом данные, которые Automator передает по каналу посредством действий потока операций, как предполагается, AppleScript-совместимы. Если это обнаруживает, что действие основывается на Objective C, однако, Automator преобразовывает его в объект Objective C, что действие может иметь дело с. Это также берет объекты, которые обеспечивают действия Objective C, и преобразовывает их в форму, подходящую для находящихся в AppleScript действий.

Указание идентификаторов типов какао

Действие, которое чисто основано на Objective C, должно иметь Специфичные для какао идентификаторы типов для AMAccepts и AMProvides свойства. В настоящее время существует три идентификатора открытого типа Какао:

Например, если необходимо было указать com.apple.cocoa.path как единственный идентификатор типа AMAccepts свойство, входной объект в runWithInput:fromAction:error: метод был бы объект NSString представление пути (или массив таких строковых объектов). Если необходимо было указать com.apple.cocoa.url как идентификатор типа AMProvides свойство, необходимо было бы возвратить объект NSURL (или массив объектов NSURL) в реализации runWithInput:fromAction:error:.

Для дальнейшего обсуждения идентификаторов типов Какао посмотрите Идентификаторы типов.

Реализация runWithInput:fromAction:error:

В Вашем пользовательском подклассе AMBundleAction необходимо переопределить метод runWithInput:fromAction:error: (наследованный от класса AMAction). Кроме указания идентификаторов типов Какао в Вашем AMAccepts и AMProvides свойства, эта реализация метода является единственным требованием для просто действие Objective C.

Если действие не должно иметь дело с входными данными, врученными его — например, его роль должна выбрать некоторые элементы в файловой системе — реализация runWithInput:fromAction:error: может возвратить то, что это разработано для обеспечения, не касаясь входных данных. В примере в Перечислении 1 действие возвращает список путей к фильмам, выбранным в его пользовательском интерфейсе (и сохраненный в его параметрах):

Перечисление 1  действие Objective C, не обрабатывающее его входной объект

- (id)runWithInput:(id)input fromAction:(AMAction *)anAction
        error:(NSDictionary **)errorInfo {
    return [[self parameters] objectForKey:@"movieFiles"];
}

Однако большинство действий воздействует на входные данные, данные их от предыдущего действия. Входной объект и выходной объект для действия являются почти всегда объектами NSArray потому что Container подсвойства AMAccepts и AMProvides списками по умолчанию (эквивалентен объектам NSArray в Objective C). Много реализаций действий Objective C runWithInput:fromAction:error: использование общего подхода, который подобен типичному on run обработчик в находящемся в AppleScript действии:

  1. Если контейнерный тип действия AMProvides свойство является Списком, подготовьте выходной массив к более позднему использованию (обычно путем создания объекта NSMutableArray).

  2. Выполните итерации через элементы входного массива, и для каждого выполняют любую работу, требуется, и запишите получающийся элемент данных в выходной массив.

  3. Возвратите выходной массив.

Перечисление 2  Реализовывая runWithInput:fromAction:error:

- (id)runWithInput:(id)input fromAction:(AMAction *)anAction error:(NSDictionary **)errorInfo
{
    NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:[input count]];
    NSEnumerator *enumerate = [input objectEnumerator];
    NSString *xmlFile;
 
    while (xmlFile = [enumerate nextObject]) {
        NSError *anError=nil;
        NSString *returnStr;
 
        NSXMLDocument *xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:xmlFile] options:nil error:&anError];
        NSLog(@"XML document = %@", [xmlDoc description]);
        if (!xmlDoc) {
            [returnArray addObject:[NSString stringWithFormat: @"ERROR: Could not make document from file: %@\n", xmlFile]];
            continue;
        }
        if (anError) {
            [returnArray addObject:[NSString stringWithFormat: @"ERROR: Error in processing file %@, because = %@\n", xmlFile, [anError localizedDescription]]];
            continue;
        }
 
        NSString *queryStr;
        if ([[[self parameters] objectForKey:@"elementAttributeChoice"] intValue]) { // attribute
            queryStr = [NSString stringWithFormat:@".//@%@/text()", [[self parameters] objectForKey:@"elementAttributeName"]];
        } else {
            queryStr = [NSString stringWithFormat:@".//%@/text()",  [[self parameters] objectForKey:@"elementAttributeName"]];
        }
        anError = nil;
        NSArray *results = [xmlDoc nodesForXPath:queryStr error:&anError];
        if (anError) {
            [returnArray addObject:[NSString stringWithFormat: @"ERROR: Error in XQuery because = %@\n", [anError localizedDescription]]];
            continue;
        }
        [returnArray addObject:[NSString stringWithFormat:@"\n--------- Found in file %@ ----------\n", xmlFile]];
        int i, count = [results count];
        for (i = 0; i < count; i++) {
            [returnArray addObject:[results objectAtIndex:i]];
        }
        [xmlDoc release];
    }
 
    return returnArray;
}

Некоторые действия в их AMAccepts свойство указывает, что их ввод является дополнительным (т.е. Optional подсвойство установлено в <true/>). Эта установка позволяет пользователям выбирать элемент «Ignore Result...» из входной кнопки всплывающего меню типа в представлении действия. Если Ваше действие сделало ввод дополнительным в AMAccepts свойство, необходимо определить, сделал ли пользователь этот выбор путем отправки действия ignoresInput сообщение; если ответ YES. Вы ничего не должны делать с входным объектом.

Если Ваше действие встречается с ошибкой, препятствующей тому, чтобы оно продолжилось, оно должно дать информацию, описывающую ошибку к Automator, тогда прекращающему выполнять поток операций и выводящему на экран ошибочное предупреждение. Последний параметр runWithInput:fromAction:error: метод является указателем на объект NSDictionary. Для создания отчетов об ошибках необходимо создать словарь, содержащий две пары ключ/значение, и возвратить этот объект в Automator косвенно. Два свойства словаря:

Поскольку пример кода в выводе Перечисления 2 является текстом, это сообщает об ошибках, таких как отсутствие допустимого документа как часть вывода. Но что, если это было фатальной ошибкой, потребовавшей, чтобы действие прекратило выполняться? Перечисление 3 иллюстрирует, как Вы могли бы обработать такую ошибку.

Перечисление 3  Сообщая о фатальной ошибке Automator

if (!xmlDoc) {
    NSArray *objsArray = [NSArray arrayWithObjects:
        [NSNumber numberWithInt:errOSASystemError],
        [NSString stringWithFormat:@”ERROR:
            Could not make document from file: %@\n", xmlFile], nil];
    NSArray *keysArray = [NSArray arrayWithObjects:OSAScriptErrorNumber,
        OSAScriptErrorMessage, nil];
    *errorInfo = [NSDictionary dictionaryWithObjects:objsArray
        forKeys:keysArray];
    return nil;
}

Ваше действие имеет всю палитру в наличии возможности к приложениям Какао. Это может передать объекты Платформы какао и пользовательские объекты от ее подкласса AMBundleAction, и это может определить и реализовать столько других классов поддержки, протоколов и категорий, сколько необходим для выполнения задачи действия. Действие Objective C могло принять решение сделать любую из следующих вещей:

Objective C имеет только два ограничения, связанные с его реализацией runWithInput:fromAction:error:. Это не может возвратиться, пока это полностью не закончило любую обработку, это инициировало. Например, если действие дает камере команду делать снимок (асинхронный процесс), оно не может возвратиться из runWithInput:fromAction:error: метод до изображения взят. Таким образом, действие должно реализовать любой алгоритм блокирования, логику тайм-аута, или поточная обработка стратегии необходима, пока не сделан снимок. Второе ограничение имеет отношение к архитектуре поточной обработки Отомэтора. Поскольку действия Objective C работают на вторичном потоке, если они хотят вывести на экран окно, это должно быть сделано на основном потоке. performSelectorOnMainThread:withObject:waitUntilDone: метод соответствует с этой целью; например:

self performSelectorOnMainThread:@selector(showPreviewWithData:) withObject:data waitUntilDone:YES]

Обновление параметров действия

Типичное действие использует технологию привязки Какао для синхронизации настроек в пользовательском интерфейсе действия с атрибутом параметров действия. (См. Построение Пользовательского интерфейса в Разработке Действия.), Но действие не требуется, чтобы использовать привязку, чтобы сделать эту синхронизацию. Это может принять решение сделать это вручную путем реализации updateParameters и parametersUpdated методы. Эти методы вызываются, пошел, пора выполнить или сохранить действие.

updateParameters метод вызывается, когда атрибут параметров действия (объект NSDictionary) должен быть обновлен от настроек и оценивает пользователя, указал в пользовательском интерфейсе. parametersUpdated метод вызывается по противоположной причине: когда то содержание изменяется программно, для создания раскрывающихся элементов кнопки, текстовые поля и другие объекты в представлении действия отражают текущее содержание параметров действия, особенно.

Перечисление 4 показывает типичную реализацию updateParameters метод.

Перечисление 4  , Обновляющее Objective C параметры действия вручную

- (void)updateParameters
{
    // text
    NSString *outputFile = [_outputFilePath stringValue];
    if (outputFile)
    {
        [[self parameters] setObject:outputFile forKey:@"outputFilePath"];
    }
 
    NSString *waitSeconds = [_waitSeconds stringValue];
    if (waitSeconds)
    {
        [[self parameters] setObject:[NSNumber numberWithInt:
            [_waitSeconds intValue]] forKey:@"waitSeconds"];
    }
 
    // radios
    [[self parameters] setObject:[NSNumber numberWithInt:
        [_interactiveType selectedRow]] forKey:@"interactiveType"];
    [[self parameters] setObject:[NSNumber numberWithInt:
        [_screenShotType indexOfSelectedItem]] forKey:@"screenshotType"];
 
    // popup
    [[self parameters] setObject:[NSNumber numberWithInt:
        [_outputType indexOfSelectedItem]] forKey:@"outputType"];
 
    // checks
    [[self parameters] setObject:[NSNumber numberWithBool:
        [_captureMainMonitorOnly state]] forKey:@"captureMainMonitorOnly"];
    [[self parameters] setObject:[NSNumber numberWithBool:
        [_disableSounds state]] forKey:@"disableSounds"];
    [[self parameters] setObject:[NSNumber numberWithBool:
        [_timedScreenshot state]] forKey:@"timedScreenshot"];
}

Можно также обновить параметры действия для включения разных данных, которые могут остаться с действием, как это, например, скопировано в область монтажа или записано в диск в потоке операций. То, когда Automator архивирует действие, он включает параметры действия с архивом, и таким образом возражает сохраненный в параметрах, может быть разархивировано и получено. Единственное ограничение - то, что объект Основы, сохраненный к параметрам действия, должен быть объектом списка свойств — т.е. NSData, NSDate, NSNumber, NSString, NSArray или объект NSDictionary. Любой объект, который не является одним из них, должен быть преобразован в объект списка свойств, прежде чем это сможет быть заархивировано.

Объекты, представляющие URLs — другими словами, экземпляры NSURL — являются важным рассматриваемым вопросом. URLs является типом данных (идентифицированный с UTI public.url) то, что действия иногда имеют дело с. К счастью, существуют методы для преобразования объектов NSURL к и от объекта списка свойств. Код в Перечислении 5 преобразовывает объект NSURL в объект NSString и хранилища это в параметрах действия.

Перечисление 5  , Сохраняющее NSURL, возражает против параметров как строковый объект

NSMutableDictionary* params;
 
if (mFilterURL) // NSURL object
{
    NSString* path = [mFilterURL path];
    params = [NSMutableDictionary dictionaryWithObject:path  forKey:@"URL" ];
 
    [self setParameters:params];
}

Теперь, когда действие архивируется, URL сохранен им, и когда оно разархивировано, URL восстановим. Фрагмент кода в Перечислении 6 восстанавливает объект NSURL от объекта NSString, хранившего в параметрах действия.

Перечисление 6  , Восстанавливающее NSURL, возражает от параметров действия

NSURL* filterURL = nil;
 
NSMutableDictionary* params = [self parameters];
 
NSString* path = [params objectForKey: @"URL"];
 
if (path) filterURL = [NSURL fileURLWithPath:path];
 
if (filterURL)
{
   // do something with filterURL here
}

Этот пример использование fileURLWithPath: создать объект NSURL представление схемы файла URL. Если бы Вы хотите объект URL с различной схемой, Вы использовали бы что-то такой как URLWithString:, метод класса NSURL. Однако строка для этого метода должна содержать любые необходимые коды Escape процента. Самый простой способ получить такую строку состоит в том, чтобы отправить absoluteString к объекту NSURL прежде, чем сохранить строку URL в параметрах действия. Если Ваше действие по некоторым причинам не имеет дело с абсолютным URLs, то оно должно разработать способ сохранить и восстановить различные части URL.

Загрузка и выполнение сценариев

Действия Objective C могут также включать код AppleScript, который они загружаются и выполняют, делая их в действительности гибридными действиями. Платформа OSA и платформа Основы содержат много классов (OSAScript, NSAppleScript, NSAppleEventDescriptor, и больше), которые включают создание, подготовку и выполнение представления объектов сценарии AppleScript.

Перечисление 7 иллюстрирует, как действие получает сценарий AppleScript или от его пользовательского интерфейса или от его параметров, создает объект OSAScript с ним, и затем компилирует и выполняет сценарий.

Перечисление 7  , Выполняющее сценарий AppleScript в действии Objective C

- (OSAScript *)script
{
    OSAScript *script = nil;
    NSString *source = nil;
 
    if ([self controller])
    {
        [[self controller] compileScript:self];
        source = [[[self controller] scriptView] string];
    }
    else
    {
        source = [self source];
    }
 
    if (source)
    {
        script = [[OSAScript alloc] initWithSource:source language:[OSALanguage defaultLanguage]];
        if (script)
        {
            NSDictionary *errorInfo;
            [script compileAndReturnError:&errorInfo];
        }
    }
    return [script autorelease];
}
 
- (NSString *)source
{
    return [[self parameters] objectForKey:@"RunScriptSource"];
}
 
- (void)setSource:(NSString *)source
{
    [[self parameters] setObject:source forKey:@"RunScriptSource"];
}