代码跳过直到NSTask完成

时间:2014-01-27 21:21:37

标签: xcode macos cocoa

我正在使用Cocoa为Mac创建一个小的碎纸机应用程序,可以安全地擦除文件。每次我的NSTask接收数据以更新某些UI元素时,我都在运行代码。问题是某些事情(文件剩余标签,将进度条设置为0,将窗口置于前台)在NSTask完成之前不起作用。在我的窗口中删除文件时会激活此代码。

- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
    int filesRemain = 0;

    NSPasteboard *pboard = [sender draggingPasteboard];

    NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
    NSMutableArray *args = [[NSMutableArray alloc] init];
    [args addObject:@"-rfv"];
    for(NSString * myStr in files) {
        [args addObject:myStr];
        filesRemain ++;
    }
    NSTask *task = [[NSTask alloc] init];

    task.launchPath = @"/usr/bin/srm";

    task.arguments  = args;

    NSPipe *pipe = [NSPipe pipe];
    [task setStandardOutput: pipe];
    [task launch];
    NSLog(@"task launched");
    while ([task isRunning])
    {
        NSData* data = [[pipe fileHandleForReading] availableData];
        NSString *string;
        string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
        if ([string hasSuffix:@"%"]) {
            NSString *numberString;
            NSScanner *scanner = [NSScanner scannerWithString:string];
            NSCharacterSet *numbers = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
            [scanner scanUpToCharactersFromSet:numbers intoString:NULL];
            [scanner scanCharactersFromSet:numbers intoString:&numberString];
            int number = [numberString intValue];
            [progIndicator setDoubleValue:number];
            [progText setStringValue:[NSString stringWithFormat:@"%d Files Remaining", filesRemain]];
        } else if (!([string rangeOfString:@"done"].location == NSNotFound)) {
            filesRemain --;
            [progIndicator setDoubleValue:100];
            if (filesRemain == 0) {
                NSLog(@"process completed");
                [dropWindow setAlphaValue:1];
                [progressView setAlphaValue:0];
            } else {
                [progIndicator setDoubleValue:0];
            }
        }
    }
    return YES;
}

有没有办法阻止此代码跳过?谢谢你的回答!

1 个答案:

答案 0 :(得分:4)

此代码存在一些问题:

⑴你在while()循环中阻塞主线程,所以在完成之前不会显示任何内容,并且应用程序将锁定并且用户将无法输入输入。您可以通过添加调用来解决这个问题 - 每次都显示有问题的视图,但请不要,因为还有其他问题,例如

⑵您可以在循环执行期间尽快轮询任务中的数据,两次尝试之间没有任何延迟,因此您绝对会烧毁用户的电池。 CPU非常快,它可以一秒钟处理数十亿个指令,因此这个循环将开始非常快速地旋转,比任务产生输出快得多,并且它将像疯了一样更新所有进度表。

⑶在请求availableData之前,您不会检查是否有可用的数据,因此您的应用程序可能会在等待任务完成时暂停一段时间。


有几种方法可以解决这些问题。最现代和最好的方法是将fileHandle的readabilityHandler设置为一个块,用于在主线程上调度UI的更新,然后立即从上面的方法返回,以便主运行循环重新获得控制并显示。

请注意(从文档中)看起来您的readabilityHandler将从任意线程调用,因此您不希望修改任何未受保护的实例变量或直接从中调用任何UI调用。你可以做的是用它来安排主线程上的UI更新,如下所示:

NSPipe *pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
[task launch];
pipe.fileHandleForReading.readabilityHandler = ^{
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSString *const string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        // ...
        // update UI code
        // ...
    };
};