在传递大量数据时解决NSFileHandle NSTask阻塞问题

Jus*_*ams 3 macos cocoa nstask

我有一些代码可以处理从我的应用程序导出数据.它接收一个充满XML的NSString,并通过PHP脚本运行它来生成HTMl,RTF等.除非用户有一个大的列表,否则它运行良好.这显然是因为它超过了8k左右的NSPipe缓冲区.

我在readPipe和readHandle中解决了这个问题(我认为),但我不确定如何在writeHandle/writePipe中处理它.[writeHandle writeData:[in...除非我在gdb中打破它,等待几秒然后继续,否则应用程序将会出现问题.

有关如何在我的代码中解决此问题的任何帮助?

- (NSString *)outputFromExporter:(COExporter *)exporter input:(NSString *)input {
  NSString *exportedString = nil;
  NSString *path = [exporter path];
  NSTask *task = [[NSTask alloc] init];

  NSPipe *writePipe = [NSPipe pipe];
  NSFileHandle *writeHandle = [writePipe fileHandleForWriting];
  NSPipe *readPipe = [NSPipe pipe];
  NSFileHandle *readHandle = [readPipe fileHandleForReading];

  NSMutableData *outputData = [[NSMutableData alloc] init];
  NSData *readData = nil;

  // Set the launch path and I/O for the task
  [task setLaunchPath:path];
  [task setStandardInput:writePipe];
  [task setStandardOutput:readPipe];

  // Launch the exporter, it will convert the raw OPML into HTML, Plaintext, etc
  [task launch];

  // Write the raw OPML representation to the exporter's input stream
  [writeHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]];
  [writeHandle closeFile];

  while ((readData = [readHandle availableData]) && [readData length]) {
    [outputData appendData:readData];
  }

  exportedString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
  return exportedString;
}
Run Code Online (Sandbox Code Playgroud)

sup*_*rin 11

自10.7以来有一个新的API,因此您可以避免使用NSNotifications.

task.standardOutput = [NSPipe pipe];
[[task.standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) {
    NSData *data = [file availableData]; // this will read to EOF, so call only once
    NSLog(@"Task output! %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

    // if you're collecting the whole output of a task, you may store it on a property
    [self.taskOutput appendData:data];
}];
Run Code Online (Sandbox Code Playgroud)

可能你想重复上面的相同task.standardError.

重要:

当您的任务终止时,您必须将readabilityHandler块设置为nil; 否则,您将遇到高CPU使用率,因为读数永远不会停止.

[task setTerminationHandler:^(NSTask *task) {

    // do your stuff on completion

    [task.standardOutput fileHandleForReading].readabilityHandler = nil;
    [task.standardError fileHandleForReading].readabilityHandler = nil;
}];
Run Code Online (Sandbox Code Playgroud)

这都是异步的(你应该做异步),所以你的方法应该有一个^完成块.