Objective c NSTask字符输出到NSTextField,作为连续流无缓冲

Objective c NSTask字符输出到NSTextField,作为连续流无缓冲,objective-c,cocoa,nstextfield,nstask,Objective C,Cocoa,Nstextfield,Nstask,我想要完成的是启动一个命令行(CL)任务(包装的NSTask)并通过UI中的NSTextField标签将字符输出管道(NSPipe),以字符流的形式实时输出。textfield的目的不是以任何方式捕获输出,甚至不允许读取输出。它只是为了显示它,一部分是作为UI装饰,另一部分是作为一种进度指示器。我希望用户在CL任务工作时看到一个字符流(快速)流过 我知道如何将CL任务包装在NSTask中,并通过设置[task setStandardOutput:outputPipe]获取其输出,然后使用NSFi

我想要完成的是启动一个命令行(CL)任务(包装的NSTask)并通过UI中的NSTextField标签将字符输出管道(NSPipe),以字符流的形式实时输出。textfield的目的不是以任何方式捕获输出,甚至不允许读取输出。它只是为了显示它,一部分是作为UI装饰,另一部分是作为一种进度指示器。我希望用户在CL任务工作时看到一个字符流(快速)流过

我知道如何将CL任务包装在NSTask中,并通过设置[task setStandardOutput:outputPipe]获取其输出,然后使用NSFileHandle读取该输出。我想我知道如何用一种NSFileHandle读取方法来实现我想要的“硬”方式,并同步地将输出分割成块,然后在文本字段中逐个显示这些块。但我希望有一种我没有想到的轻量级方式,可以将原始ascii字符从标准输出实时地发送到文本字段中

有人有主意吗

编辑:以下是一些基于@Peter Hosey答案的工作代码。它正在做我想做的事情,但我不知道我是否彻底理解了彼得的概念,或者我在这里做了什么不可靠的事情,所以请随意评论。再次感谢彼得

关于此代码的注释:

1) 将init中的scheduledTimerWithTimeInterval从.001更改为.005是文本滚动效果的一个有趣的可视范围

2) 我使用的标签只是在interface builder的UI上创建的一个简单文本标签。出于我的目的,我不需要使用正确的字符串来完成Peter答案的第二部分。我只是在interface builder中设置文本标签的对齐方式

@interface MyWrapper : NSObject

@property (assign) NSMutableData *_outputData;
@property (assign) NSFileHandle *_fileHandle;
@property (assign) IBOutlet NSTextField *label;
@property (assign) NSTimer *_timer;

-(void) readData:(NSNotification *)notification;
-(void) displayOutput;
-(void) doIt;

@end

@implementation MyWrapper

@synthesize _outputData, _fileHandle, label, _timer;

- (id)init {
    self = [super init];
    if (self) {

      [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector( readData: )
                                                   name:NSFileHandleReadCompletionNotification 
                                                 object:nil];
      _outputData = [[NSMutableData alloc] initWithCapacity:300];
      _timer = [NSTimer scheduledTimerWithTimeInterval:.001 
                                               target:self 
                                             selector:@selector(displayOutput) 
                                             userInfo:nil 
                                              repeats:YES];
    }

    return self;
}
- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];  
  [_timer invalidate];
  [super dealloc];
}

-(void) readData:(NSNotification *)notification {

  if( [notification object] != _fileHandle )
    return;

  [_outputData appendData:[[notification userInfo] 
          objectForKey:NSFileHandleNotificationDataItem]];

  [_fileHandle readInBackgroundAndNotify];
}

-(void) displayOutput {

  if ([_outputData length] == 0) {
    return;
  }

  NSString *labelText = [label stringValue];
  NSData *nextByte;
  NSString *nextChar;

  // pull first character off of the outputData
  nextByte = [_outputData subdataWithRange:NSMakeRange(0, 1)];
  nextChar = [[NSString alloc]initWithData:nextByte
                                   encoding:NSASCIIStringEncoding];

  // get rid of first byte of data
  [_outputData replaceBytesInRange:NSMakeRange(0, 1) withBytes:NULL length:0];

  if (! [nextChar isEqualToString:@"\n"]) {
    if ([labelText length] > 29) {
      labelText = [labelText substringFromIndex:1];
    }

    labelText = [labelText stringByAppendingString:nextChar];
    [label setStringValue:labelText];
  }  
}

-(void)doIt {

  NSTask *theTask = [[NSTask alloc] init];
  NSPipe *outPipe =[NSPipe pipe];
  //write output to outputData in background
  _fileHandle = [outPipe fileHandleForReading];
  [_fileHandle readInBackgroundAndNotify];

  [theTask setLaunchPath:@"path/to/executable"];
  [theTask setStandardOutput:outPipe];
  [theTask setStandardError:[NSPipe pipe]];
  [theTask launch];
  [theTask waitUntilExit];  
}

@end
,a,一个NSMutableData,通过仅保留最后一个字节并删除旧字节,以及在文本字段中正确对齐,将其限制为固定的字节数(比如300)


在最后一部分中,您需要创建文本字段的可变副本,并将其设置为具有段落样式的属性字符串。

[皱眉]您是说使用计时器定期向NSMutableData添加新字节,从NSMutableData中删除旧字节,然后在文本字段中重新显示NSMutableData?我在思考如何将所有这些连接起来,但我会尝试一下,并用一些代码返回报告。谢谢你花时间回复。@pjv:差不多了。您向NSMutableData添加字节以响应来自文件句柄的通知;您可以修剪并更新文本字段以响应计时器触发。