0

I'm running a command via NSTask that generates a very large amount of output. I created a pipe connected to standard output and am using waitForDataInBackgroundAndNotify and consuming the data with availableData. However it appears that all of the output is being buffered as the memory allocated by the app grows continuously. How can I purge / consume the data being sent to stdout? Here is the code I'm using:

- (void)runCommand:(NSString *)commandToRun
{
    self.task = [[NSTask alloc] init];
    [self.task setLaunchPath: @"/bin/sh"];
    NSArray *arguments = [NSArray arrayWithObjects:
                          @"-c" ,
                          [NSString stringWithFormat:@"%@", commandToRun],
                          nil];
    [self.task setArguments: arguments];
    NSPipe *pipe = [NSPipe pipe];
    [self.task setStandardOutput:pipe];
    NSFileHandle *fh = [pipe fileHandleForReading];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedData:) name:NSFileHandleDataAvailableNotification object:fh];
    [fh waitForDataInBackgroundAndNotify];
    [self.task launch];
}

- (void)receivedData:(NSNotification *)notification
{
    NSFileHandle *fh = [notification object];
    NSData *data = [fh availableData];
    // do stuff...
    [fh waitForDataInBackgroundAndNotify];
}
stdout
  • 1,761
  • 1
  • 18
  • 33
  • 1
    What does your `do stuff` actually do with `data`? I suspect it's keeping strong references to it or copying it into other in-memory data structures. – Ken Thomases Apr 12 '13 at 00:40
  • Sure enough when I commented out all the `stuff` memory allocations no longer grew, so you are right is was not an NSTask/NSPipe/etc. issue. But I could not see where I was retaining any references to `data`. Encasing the contents of `receivedData` in an `@autoreleasepool` resolved the issue. I wonder if ARC decided to keep all the allocations around until `task` was released (I just editied the code in my question to reflect that `task` is really retained property)? Or is it possible that `@autoreleasepool` just now masking a problem that is still there? – stdout Apr 12 '13 at 13:19
  • @stdout: The only way autoreleasing can “mask” a memory problem is if your problem is that you allocate a bunch of memory at once; autoreleasing will help you get rid of it, but your “high water mark”, as Apple calls it, would still be high. It won't “mask” a leak. Are you using ARC? And, again, as Ken Thomases already asked, what are you doing in “do stuff”? – Peter Hosey Apr 12 '13 at 18:23
  • Another reason that `@autoreleasepool` might help is if nothing is regularly draining an autorelease pool outside of the methods shown. For example, if this is not called by the main thread of an app but by a custom thread or a command-line tool. Obviously, a run loop is being run (since data is being received), but not necessarily in such a way that there's a surrounding autorelease pool that gets drained. – Ken Thomases Apr 13 '13 at 09:03

0 Answers0