gngrwzrd

NSTask Runner with NSPipes for StdIn/StdOut/StdErr

Jul 17th, 2014
250
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //// header
  2. #import <Foundation/Foundation.h>
  3. @class SCTaskRunner;
  4. @protocol SCTaskRunnerDelegate <NSObject>
  5. @optional
  6. - (void) taskRunner:(SCTaskRunner *) taskRunner didReadData:(NSData *) data;
  7. - (void) taskRunner:(SCTaskRunner *) taskRunner didReadDataFromStdOut:(NSData *) data;
  8. - (void) taskRunner:(SCTaskRunner *) taskRunner didReadDataFromStdErr:(NSData *) data;
  9. - (void) taskRunnerTerminated:(SCTaskRunner *) taskRunner;
  10. @end
  11.  
  12. @interface SCTaskRunner : NSObject {
  13.     BOOL _terminated;
  14. }
  15. @property (assign) NSObject <SCTaskRunnerDelegate> * delegate;
  16. @property BOOL collectDataUntilTerminated;
  17. @property NSTask * task;
  18. @property NSPipe * standardInput;
  19. @property NSPipe * standardOutput;
  20. @property NSPipe * standardError;
  21. @property NSMutableData * data;
  22. - (id) initWithStandardInput:(NSPipe *) pipe;
  23. @end
  24.  
  25. ////implementation
  26.  
  27. #import "SCTaskRunner.h"
  28.  
  29. @implementation SCTaskRunner
  30.  
  31. - (id) init {
  32.     self = [super init];
  33.     self.task = [[NSTask alloc] init];
  34.     self.data = [NSMutableData data];
  35.     [self setupPipesAndNotifications];
  36.     return self;
  37. }
  38.  
  39. - (id) initWithStandardInput:(NSPipe *) standardInput {
  40.     self = [self init];
  41.     self.standardInput = standardInput;
  42.     return self;
  43. }
  44.  
  45. - (void) setupPipesAndNotifications {
  46.     self.standardOutput = [[NSPipe alloc] init];
  47.     self.standardError = [[NSPipe alloc] init];
  48.     self.task.standardOutput = self.standardOutput;
  49.     self.task.standardError = self.standardError;
  50.    
  51.     NSNotificationCenter * nfc = [NSNotificationCenter defaultCenter];
  52.    
  53.     //add notification to task for termination
  54.     [nfc addObserver:self selector:@selector(taskTerminated:) name:NSTaskDidTerminateNotification object:self.task];
  55.    
  56.     //add notifications for read data on standard out and standard error
  57.     [nfc addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleDataAvailableNotification object:self.standardOutput.fileHandleForReading];
  58.     [nfc addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleDataAvailableNotification object:self.standardError.fileHandleForReading];
  59.     [self.standardOutput.fileHandleForReading waitForDataInBackgroundAndNotify];
  60.     [self.standardError.fileHandleForReading waitForDataInBackgroundAndNotify];
  61.    
  62.     if(self.standardInput) {
  63.         self.task.standardInput = self.standardInput;
  64.     }
  65. }
  66.  
  67. - (void) dataAvailableForFileHandle:(NSFileHandle *) fileHandle {
  68.     NSData * data = [fileHandle availableData];
  69.    
  70.     if(data.length > 0) {
  71.         if(self.collectDataUntilTerminated) {
  72.             [self.data appendData:data];
  73.         } else {
  74.            
  75.             if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadData:)]) {
  76.                 [self.delegate taskRunner:self didReadData:data];
  77.             }
  78.            
  79.             if(fileHandle == self.standardOutput.fileHandleForReading) {
  80.                 if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadDataFromStdOut:)]) {
  81.                     [self.delegate taskRunner:self didReadDataFromStdOut:data];
  82.                 }
  83.             }
  84.            
  85.             if(fileHandle == self.standardError.fileHandleForReading) {
  86.                 if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunner:didReadDataFromStdErr:)]) {
  87.                     [self.delegate taskRunner:self didReadDataFromStdErr:data];
  88.                 }
  89.             }
  90.         }
  91.     }
  92.    
  93.     if(!_terminated) {
  94.         [fileHandle waitForDataInBackgroundAndNotify];
  95.     }
  96. }
  97.  
  98. - (void) dataAvailable:(NSNotification *) notification {
  99.     NSFileHandle * fileHandle = notification.object;
  100.     [self dataAvailableForFileHandle:fileHandle];
  101. }
  102.  
  103. - (void) taskTerminated:(NSNotification *) notification {
  104.     _terminated = TRUE;
  105.     [self dataAvailableForFileHandle:self.standardOutput.fileHandleForReading];
  106.     [self dataAvailableForFileHandle:self.standardError.fileHandleForReading];
  107.     if(self.delegate && [self.delegate respondsToSelector:@selector(taskRunnerTerminated:)]) {
  108.         [self.delegate taskRunnerTerminated:self];
  109.     }
  110. }
  111.  
  112. - (void) dealloc {
  113.     NSLog(@"DEALLOC: SCTaskRunner");
  114.     [[NSNotificationCenter defaultCenter] removeObserver:self];
  115.     [self.task terminate];
  116.     self.task = nil;
  117.     self.delegate = nil;
  118.     self.standardOutput = nil;
  119.     self.standardError = nil;
  120.     self.standardInput = nil;
  121. }
  122.  
  123. @end
Advertisement
Add Comment
Please, Sign In to add comment