可可等待完成执行

时间:2014-03-12 19:49:04

标签: objective-c multithreading macos cocoa

'在运行多个包含NSTask的方法运行应用程序包中的脚本时遇到问题(使用GDC更新UI)。

问题是如何一个接一个地使用,即仅在前一个完成时使用。 我说的每个方法都使用NSTask来运行sh脚本,这些脚本的持续时间为几秒,甚至可能超过一分钟(取决于完成的计算)。

我试过的一些事情很遗憾地阻止了UI,这是不正确的,因为即使是“停止”按钮也被锁定。 我正在调用以下方法:

[self performSelectorOnMainThread:@selector(runScript1:) withObject:blabla waitUntilDone:YES];
//some if statements here
[self performSelectorOnMainThread:@selector(runScript2:) withObject:blabla waitUntilDone:YES]; how to wait for runScript1 completition??
//more script to call after

编辑(添加runScript1的示例)

    - (void)runScrip1:(NSArray*)args
{
    if (_operationPermitted == 1) {
        NSString *acq, *path;

        acq = [args objectAtIndex:0];
        path = [args objectAtIndex:1];

        dispatch_queue_t runQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
        dispatch_async(runQueue, ^{

            @try {
                runScrip1Task = [[NSTask alloc] init];

                Scrip1 = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Scrip1.sh"];
                [_runScrip1sTask setLaunchPath:Scrip1];
                [_runScrip1Task setArguments:[NSArray arrayWithObjects:@"skip", @"main", acq, path, nil]];

                [_runScrip1Task setCurrentDirectoryPath:@"/private/tmp"];
                [tasks addObject:_runScrip1Task];

                self.outputPipe               = [[NSPipe alloc] init];
                _runScrip1Task.standardOutput = self.outputPipe;
                _runScrip1Task.standardError  = self.outputPipe;

                [[self.outputPipe fileHandleForReading] waitForDataInBackgroundAndNotify];

                [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification object:[self.outputPipe fileHandleForReading] queue:nil usingBlock:^(NSNotification *notification){


                    self.display2.string = @"\nAcquiring all the data...";
                    NSData *output = [[self.outputPipe fileHandleForReading] availableData];
                    NSString *outPutString = [[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding];

                    // ----------------------------------------------- //

                    // here some code to update my UI

                    // ----------------------------------------------- //

                    dispatch_sync(dispatch_get_main_queue(), ^{
                        // manage text sobstitutions
                        [_outputView appendAttributedString:[[NSAttributedString alloc] initWithString:outPutString attributes:_whiteTextwithArialFont]];
                        [_displayCenter scrollToEndOfDocument:self];
                    });

                    [[self.outputPipe fileHandleForReading] waitForDataInBackgroundAndNotify];
                }];

                [_runScrip1TaskTask launch];
                [_runScrip1TaskTask waitUntilExit];

            }
            // handle errors w/o app crashes
            @catch (NSException *exception) {
                NSLog(@"Problem Running Task: %@", [exception description]);
                [self stopAllRunningsTasks];
            }
            @finally {
                _operationPermitted = YES;
                _finished = true;

            }
            if (_runScrip1TaskTask.terminationStatus > 0) {
                [self stopAllRunningsTasks];
            }
        });
    }
}

任何人都可以指引我以正确的方式,而不会阻止用户界面吗? 感谢

2 个答案:

答案 0 :(得分:1)

解决方案是让脚本保持异步,因为它们实际上是固有的,并使用“continuation”。

以下示例代码应提供一些帮助:

typedef void (^completion_t)(int result);

- (void) runScript1WithParams:(NSArray*)params completion:(completion_t)completion {
   // Create and setup the task:
   NSTask* task = [[NSTask alloc] init];
   ...
   [[NSNotificationCenter defaultCenter] addObserverForName:NSFileHandleDataAvailableNotification object:[self.outputPipe fileHandleForReading] queue:nil usingBlock:^(NSNotification *notification){
       ...
   }];

   // set termination handler:
   task.terminationHandler = ^(NSTask* task){
        if (completion) {
            completion(task.terminationStatus);
        }
   };

   // handle errors if required
   ...

   // start the task asynchronously:
   [task launch];
}

您的其他runScript方法以相同的方式实现。

用法:

[self runScript1WithParams:params completion:^(int result){
    if (result == 0) {
        [self runScript2WithParams:params completion:^(int result){
            if (result == ...) {
                ...
            }
            else {
                ...
            }
        }
    }
}];

答案 1 :(得分:0)

这样做你想要的吗?

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self runScript1:blabla];
    [self runScript2:blabla];
});

或者,您可以创建自己的串行调度队列或NSOperationQueue,其最大并发操作数为1并使用它。