我正在使用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;
}
有没有办法阻止此代码跳过?谢谢你的回答!
答案 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
// ...
};
};