7

我一直在尝试使用最近的 NSUserScriptTask 类及其子类来解决(参见thisthis ),到目前为止,我已经解决了一些问题,但还有一些问题有待解决。文档中可以看出,NSUserScriptTask 不允许取消任务。因此,我决定创建一个简单的可执行文件,它将脚本路径作为参数并运行脚本。这样,我可以使用 NSTask 从我的主应用程序启动帮助程序,并[task terminate]在必要时调用。但是,我要求:

  • 从它启动的助手接收输出和错误的主应用程序
  • 助手仅在 NSUserScriptTask 完成时终止

主应用程序的代码很简单:只需使用正确的信息启动一个 NSTask。这是我现在所拥有的(为了简单起见,我忽略了安全范围书签等的代码,这些都没有问题。但不要忘记这是运行沙盒):

// Create task
task = [NSTask new];
[task setLaunchPath: [[NSBundle mainBundle] pathForResource: @"ScriptHelper" ofType: @""]];
[task setArguments: [NSArray arrayWithObjects: scriptPath, nil]];

// Create error pipe
NSPipe* errorPipe = [NSPipe new];
[task setStandardError: errorPipe];

// Create output pipe
NSPipe* outputPipe = [NSPipe new];
[task setStandardOutput: outputPipe];

// Set termination handler
[task setTerminationHandler: ^(NSTask* task){        
    // Save output
    NSFileHandle* outFile = [outputPipe fileHandleForReading];
    NSString* output = [[NSString alloc] initWithData: [outFile readDataToEndOfFile] encoding: NSUTF8StringEncoding];

    if ([output length]) {
        [output writeToFile: outputPath atomically: NO encoding: NSUTF8StringEncoding error: nil];
    }

    // Log errors
    NSFileHandle* errFile = [errorPipe fileHandleForReading];
    NSString* error = [[NSString alloc] initWithData: [errFile readDataToEndOfFile] encoding: NSUTF8StringEncoding];

    if ([error length]) {
        [error writeToFile: errorPath atomically: NO encoding: NSUTF8StringEncoding error: nil];
    }

    // Do some other stuff after the script finished running <-- IMPORTANT!
}];

// Start task
[task launch];

请记住,我需要终止处理程序仅在以下情况下运行:(a)任务被取消(b)任务因脚本完成运行而自行终止。

现在,在助手方面,事情开始变得棘手,至少对我来说是这样。为了简单起见,让我们假设脚本是一个 AppleScript 文件(所以我使用 NSUserAppleScriptTask 子类 - 在现实世界中,我必须适应这三种类型的任务)。这是我到目前为止得到的:

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        NSString* filePath = [NSString stringWithUTF8String: argv[1]];
        __block BOOL done = NO;

        NSError* error;
        NSUserAppleScriptTask* task = [[NSUserAppleScriptTask alloc] initWithURL: [NSURL fileURLWithPath: filePath] error: &error];

        NSLog(@"Task: %@", task); // Prints: "Task: <NSUserAppleScriptTask: 0x1043001f0>" Everything OK

        if (error) {
            NSLog(@"Error creating task: %@", error); // This is not printed
            return 0;
        }

        NSLog(@"Starting task");

        [task executeWithAppleEvent: nil completionHandler: ^(NSAppleEventDescriptor *result, NSError *error) {
            NSLog(@"Finished task");

            if (error) {
                NSLog(@"Error running task: %@", error);
            }

            done = YES;
        }];

        // Wait until (done == YES). How??
    }

    return 0;
}

现在,我有三个问题(这是我想通过这个 SO 条目提出的问题)。首先,“已完成的任务”永远不会被打印出来(块永远不会被调用),因为任务甚至永远不会开始执行。相反,我在控制台上得到了这个:

MessageTracer: msgtracer_vlog_with_keys:377: odd number of keys (domain: com.apple.automation.nsuserscripttask_run, last key: com.apple.message.signature)

我尝试从主应用程序运行完全相同的代码,并且它毫不费力地完成(但从主应用程序我失去了取消脚本的能力)。

其次,我只想在return 0;调用完成处理程序后到达 main() 的末尾。但我不知道该怎么做。

第三,每当助手出现错误或输出时,我想将该错误/输出发送回应用程序,应用程序将通过 errorPipe/outputPipe 接收它们。类似的东西fprintf(stderr/stdout, "string")可以解决问题,但我不确定这是否是正确的方法。

因此,简而言之,感谢有关第一个和第二个问题的任何帮助。第三个我只是想确保我应该这样做。

谢谢

4

2 回答 2

4

问题一:子任务没有运行,因为它的父任务立即退出。(关于“奇数个键”的日志消息是 中的一个错误NSUserScriptTask,发生是因为您的助手没有捆绑标识符,但在其他方面无害且与您的问题无关。)它立即退出,因为它没有等待完成阻止火灾,这使我们...

问题2:如何等待异步完成块?这已在其他地方得到解答,包括等待多个网络请求全部执行 - 包括它们的完成块,但回顾一下,使用调度组,如下所示:

dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
[task executeWithAppleEvent:nil completionHandler:^(NSAppleEventDescriptor *result, NSError *e) {
    ...
    dispatch_group_leave(g);
}];
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
dispatch_release(g);

同样的模式适用于任何具有您想要等待的完成块的调用。如果您希望在组完成时收到另一个通知,而不是等待它,请使用dispatch_group_notify代替dispatch_group_wait.

于 2013-03-30T23:11:58.963 回答
3

作为旁注,您error在分配 后进行测试的方式NSUserAppleScriptTask不正确。当且仅当函数结果为 nil(或 NO,或任何指示失败)时,才error定义的值。如果函数成功(你知道它是否返回非 nil),那么可能是任何东西——函数可能将它设置为 nil,它可能让它未定义,它甚至可能用一个真实的对象填充它。(另请参阅(NSError**) 错误的意义何在?error

于 2013-03-31T17:57:05.800 回答