简体   繁体   中英

Running NSTask inside a loop in Objective-C

I am trying to run a simple shell command that runs and returns text rather quickly inside of a loop of indeterminate size at compile time that is generated by an NSArray. In scripting languages like perl, I would be able to do something like this:

for(i=0;i<=$myinputarraysize;i++){
    $output[i]=`/my/task $inputarray[i]`;
}

This would build a new array for me from the expected output of my task. In Obj-C this seems to be much more difficult and a bit confusing to me. Right now my loop looks like this:

for(int i=0; i<[inputarray count]; i++){
    NSTask *task;
    task = [[NSTask alloc] init];
    [task setLaunchPath:nsdchat];

    NSArray *args;
    args = [NSArray arrayWithObjects:@"/my/task", [inputarray objectAtIndex:i], nil];
    [task setArguments:args];

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

    NSFileHandle *file;
    file = [pipe fileHandleForReading];

    [task launch];

    NSData *data;
    data = [file readDataToEndOfFile];

    NSString *desc;
    desc = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];    
    desc = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];

    [descriptions insertObject:desc atIndex:i];

    [task release];
    [args release];
    [pipe release];
    [file release];
    [data release]; 
}

My goal is to fill descriptions (an NSMutableArray) with the output from my task (which I know is always a string and always ends in a newline that I want to strip out). It seems I'm missing something about memory releasing as when I run this and NSLog the output, I get the same result for the entire count of the loop.

Is there any easier or more way to loop through simple tasks like this? Am I overcomplicating it for myself?

First of all, there's no need to do most of that work inside the loop. task , pipe and file all look like they could be handled outside the loop. You should also consider using Objective-C 2.0 dot syntax and fast enumeration to cut things down a little.

More significantly:

NSArray *args;
args = [NSArray arrayWithObjects:@"/my/task", [inputarray objectAtIndex:i], nil];
[task setArguments:args];

This says that the first argument passed to the executable at path ndschat is /my/task . Which doesn't seem to match your PERL usage. Probably you want just:

NSArray *args;
args = [NSArray arrayWithObject:[inputarray objectAtIndex:i]];
[task setArguments:args];

Or, with the style comments taken into account:

for(NSString *argument in inputarray)
{
    ...
    task.arguments = [NSArray arrayWithObject:argument];
    ...
}

EDIT: you're also releasing a lot of objects you don't own, which as well as adding heft to your code is a memory management error possibly leading to a crash. So, to cut the whole thing down and correct that fault:

for(NSString *argument in inputarray)
{
    NSTask *task = [[NSTask alloc] init];  // you now own this
    task.launchPath = nsdchat;

    NSPipe *pipe = [NSPipe pipe];          // you don't own this
    task.standardOutput = pipe;

    NSFileHandle *file = [pipe fileHandleForReading];  // you also don't own this

    task.arguments = [NSArray arrayWithObject:argument];
    [task launch];
    [task waitUntilExit]; // you should wait until the task is done

    NSData *data = [file readDataToEndOfFile];  // this is another thing
                                                // you don't own. Note also that
                                                // readDataToEndOfFile advances
                                                // the current read pointer, so
                                                // it should be fine to do this
                                                // successively 

    NSString *desc;
    desc = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] 
                                                                   autorelease];    
                                                // you don't own this because
                                                // of the autorelease; you
                                                // don't want to own it since
                                                // the next line will throw
                                                // it away


    desc = [string stringByReplacingOccurrencesOfString:@"\n" withString:@""];
                                                // you don't own this

    [descriptions addObject:desc];
    [task release];
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM