我一直试图用最近的NSUserScriptTask类及其子类来做(看this和this),到目前为止,我已经解决了一些问题,但是其他一些问题还有待解决。从docs可以看出,NSUserScriptTask不允许取消任务。因此,我决定创建一个简单的可执行文件,将脚本的路径作为参数并运行脚本。这样,我可以使用NSTask从我的主应用程序启动帮助程序,并在必要时调用[task terminate]
。但是,我要求:
主应用程序的代码很简单:只需使用正确的信息启动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条目的问题)。 首先,&#34;完成任务&#34;永远不会打印(块永远不会被调用),因为任务甚至从未开始执行。相反,我在我的控制台上得到了这个:
MessageTracer: msgtracer_vlog_with_keys:377: odd number of keys (domain: com.apple.automation.nsuserscripttask_run, last key: com.apple.message.signature)
我尝试从主应用程序运行完全相同的代码,它完成没有大惊小怪(但从主应用程序我失去了取消脚本的能力)。
其次,我只想在调用完成处理程序后到达main(return 0;
)的末尾。但我不知道该怎么做。
Thridly ,只要有来自帮助程序的错误或输出我想将该错误/输出发送回应用程序,该应用程序将通过errorPipe / outputPipe接收它们。像fprintf(stderr/stdout, "string")
之类的东西可以解决问题,但我不确定这是否是正确的方法。
因此,简而言之,对第一和第二个问题的任何帮助都表示赞赏。第三个我只是想确定我应该怎么做。
由于
答案 0 :(得分:4)
问题1:子任务未运行,因为其父项立即退出。 (关于“奇数个键”的日志消息是NSUserScriptTask
中的错误,并且因为您的帮助程序没有包标识符而发生,否则无害且与您的问题无关。)它会立即退出,因为它是没有等待完成块开火,这让我们... ...
问题2:您如何等待异步完成块?这已在其他地方得到解答,包括Wait until multiple networking requests have all executed - including their completion blocks,但回顾一下,使用调度组,如下所示:
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
。
答案 1 :(得分:3)
作为旁注,分配error
后测试NSUserAppleScriptTask
的方式不正确。当且仅当函数结果为nil(或NO或其他表示失败)时,error
的值才定义为。如果函数成功(你知道它是否返回非零值),那么error
可能是任何东西 - 函数可能将其设置为nil,它可能会使其保持未定义,甚至可能用真实函数填充它宾语。 (另见What's the Point of (NSError**)error?)