views:

122

answers:

2

Hi,

I've been spending the last few hours trying to find the memory leak in my code. Here it is:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

expression = [expression stringByTrimmingCharactersInSet:
              [NSCharacterSet whitespaceAndNewlineCharacterSet]]; // expression is an NSString object.

NSArray *arguments = [NSArray arrayWithObjects:expression, [@"~/Desktop/file.txt" stringByExpandingTildeInPath], @"-n", @"--line-number", nil];
NSPipe *outPipe = [[NSPipe alloc] init];

NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:@"/usr/bin/grep"];
[task setArguments:arguments];
[task setStandardOutput:outPipe];
[outPipe release];

[task launch];

NSData *data = [[outPipe fileHandleForReading] readDataToEndOfFile];

[task waitUntilExit];
[task release];

NSString *string = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding];
string = [string stringByReplacingOccurrencesOfString:@"\r" withString:@""];

int linesNum = 0;

NSMutableArray *possibleMatches = [[NSMutableArray alloc] init];

if ([string length] > 0) {

    NSArray *lines = [string componentsSeparatedByString:@"\n"];
    linesNum = [lines count];

    for (int i = 0; i < [lines count]; i++) {

        NSString *currentLine = [lines objectAtIndex:i];
        NSArray *values = [currentLine componentsSeparatedByString:@"\t"];

        if ([values count] == 20)
            [possibleMatches addObject:currentLine];
    }
}
[string release];
[pool release];

return [possibleMatches autorelease];

I tried to follow the few basic rules of Cocoa memory management, but somehow there still seems to be a leak, I believe it's an array that's leaking. It's noticeable if possibleMatches is large. You can try the code by using any large file as "~/Desktop/file.txt" and as expression something that yields many results when grep-ing.

What's the mistake I'm making?

Thanks for any help!

-- Ry

EDIT: I just used the Clang Static Analyzer to find leaks in my code, but it doesn't find any. It only finds dead initializations, but those can't be responsible for my leaks...

+1  A: 

Here:

NSString *string = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding];
string = [string stringByReplacingOccurrencesOfString:@"\r" withString:@""];

You're overwriting the string object pointer without releasing or autoreleasing the original string. Instead of releasing string at the end of the method, do:

NSString *string = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease];
string = [string stringByReplacingOccurrencesOfString:@"\r" withString:@""];
Wevah
Thanks, but the leak is still there.
What does `leaks <yourappname>` tell you?
Wevah
Also, if you set the `MallocStackLogging` environment variable, you should be able to see a trace of where, exactly, your leak is being allocated.
Wevah
$ leaks extractorProcess 8279: 2685 nodes malloced for 543 KBProcess 8279: 1 leak for 128 total leaked bytes.Leak: 0x100212e70 size=128 zone: DefaultMallocZone_0x100010000 string '/Developer/Applications/Xcode.app/Contents/PlugIns/GDBMIDebugging.xcplugin/Contents/Resources/PBGDBIntrospectionSupport.A.dylib'Not too sure this is useful output. Using Instruments with Leaks it doesn't find anything. But that can't be true because in my program (a big loop, really) every object that gets allocated should get deallocated, too, so there must a be leak somewhere...
Strange, that doesn't look like your leak. :S (I could be wrong, of course.)
Wevah
Yeah, it doesn't. It's a somewhat bizarre leak, too because sometimes it leaks more than at other times, without apparent reason. The only thing I can suspect is that the leak has to do with the array because the leak gets bigger when the array grows larger.
A: 

A probable route to a solution is to use Instruments' Leaks template. By setting the inspection range to start just before this code, then looking at which allocations are still alive after this code has returned, you can see what got leaked, and then look at the leaks' pointer histories and the corresponding stack traces to see exactly what happened.

Peter Hosey