Objective-C,NSTask缓冲区限制

时间:2019-03-21 06:45:00

标签: ios objective-c xcode macos nstask

我正在使用NSTask运行一个外部实用程序,该实用程序返回一长串数据。问题在于,当返回的字符串超过大量数据(大约32759个字符)时,它将变为null或截断返回的字符串。如何返回完整的输出?

NSTask *myTask = [[NSTask alloc] init];

[myTask setLaunchPath:myExternalCommand];
[myTask setArguments:[NSArray arrayWithObjects: arg1, arg2, nil]];

NSPipe *pipe = [NSPipe pipe];
[myTask setStandardOutput:pipe];

NSFileHandle *taskHandle;
taskHandle = [pipe fileHandleForReading];

[myTask launch];
[myTask waitUntilExit];

NSData *taskData;
taskData = [taskHandle readDataToEndOfFile];

NSString *outputString = [[NSString alloc] initWithData:taskData
                         encoding:NSUTF8StringEncoding];

NSLog(@"Output: \n%@", outputString);
// (null or truncated) when stdout exceeds x amount of stdout

要测试功能,请在大文件上使用cat或类似的myExternalCommand。该问题似乎在字符长度为32759 ...之后立即发生。

解决方案?我不确定,但是可能需要做的是以某种方式读取块中的返回stdout,然后在可能的情况下附加outputString数据。

更新:我尝试根据每个建议在waitUntilExit之后移动readDataToEndOfFile,但这并没有影响结果。

  

*请注意,谢谢您正在寻找Obj-C解决方案。

2 个答案:

答案 0 :(得分:4)

CocoaDev上找到:

  

“通过管道的数据被缓冲;缓冲区的大小由底层操作系统决定。”

     

来自:http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSPipe_Class/index.html

     

NSPipe缓冲区限制似乎是4096个字节(参见/usr/include/limits.h:“… #define _POSIX_ARG_MAX 4096…”)

您可以使用readabilityHandler异步读取NSTask的输出。在处理程序中,使用availableData逐段读取输出。

任务结束后,使用terminationHandler接收通知,然后将readabilityHandler设置为nil以阻止其读取。

这都是异步的,因此您需要阻止并等待任务退出。

这里有一个完整的样本,对我来说效果很好。我使用了printf而不是NSLog,因为看来NSLog正在截断控制台上的输出(不确定这是否是bug or a feature)。错误检查被省略并增加了一些复杂性,您可能还希望以相同的方式阅读standardError

dispatch_semaphore_t waitHandle;
NSTask *myTask;
NSMutableData* taskOutput;

waitHandle = dispatch_semaphore_create(0);

myTask = [[NSTask alloc] init];
[myTask setLaunchPath:@"/bin/cat"];
[myTask setArguments:[NSArray arrayWithObjects: @"/path/to/a/big/file", nil]];
[myTask setStandardOutput:[NSPipe pipe]];

taskOutput = [[NSMutableData alloc] init];

[[myTask.standardOutput fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) {
    NSData *data = [file availableData];
    [taskOutput appendData:data];
}];

[myTask setTerminationHandler:^(NSTask *task) {
    [task.standardOutput fileHandleForReading].readabilityHandler = nil;

    NSString *outputString = [[NSString alloc] initWithData:taskOutput encoding:NSUTF8StringEncoding];
    printf("Output: \n%s\n", [outputString UTF8String]);

    dispatch_semaphore_signal(waitHandle);
}];

[myTask launch];

dispatch_semaphore_wait(waitHandle, DISPATCH_TIME_FOREVER);

答案 1 :(得分:0)

我有一个任务,该任务立即引发大错误,并导致挂起,在stderr中添加了读取器,从而解决了该问题

[[myTask.standardError fileHandleForReading] setReadabilityHandler:^(NSFileHandle *file) {
    NSData *data = [file availableData];
    [taskError appendData:data];
}];