views:

830

answers:

2

Hi! Help me please with the following problem:

- (NSDictionary *)getGamesList
{
    NSMutableDictionary *gamesDictionary = [[NSMutableDictionary dictionary] retain];
// I was trying to change this on the commented code below, but did have no effect
//    NSMutableDictionary *gamesDictionary = [[NSMutableDictionary alloc] init];
//    [gamesDictionary retain];
    while (sqlite3_step(statement) == SQLITE_ROW)
    {
        NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
        NSArray *gameDate = [key componentsSeparatedByString:@" "];
        NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
        NSString *date_time = [NSString stringWithFormat:@"%@, %@",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
        if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
        [[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
        [_id release];
    }
    sqlite3_reset(statement);
    return gamesDictionary;
}

The leak starts in another method of another class, there the getGamesList method is called, like this:

NSMutableDictionary *gamesDictionary;
gamesDictionary = [[NSMutableDictionary dictionaryWithDictionary:[appDelegate getGamesList]] retain];

After that there are a lot of leaks that points to NSCFArray in the string:

NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain]; 

in this method:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
    if ([keys count] != 0)    return [[keys objectAtIndex:section] uppercaseString];
    return @"";
}

I assume these things are connected to each other, but I still can not understand all of the memory management tips. Thanks a lot!

+2  A: 

Didn't use Cocoa for years (that's why I can't tell you an exact answer :/). But I guess your problem is that you systematically use retain on your objects.

Since the object reference count never get to 0, all dictionaries are keep in memory and not freed.

Try to remove the retain on [NSArray arrayWithArray] and [NSMutableDictionary dictionaryWithDictionary

http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_beginners/Some_Cocoa_essential_principles#Retain_and_Release

Romuald Brunet
+1  A: 

It does look like you are over-retaining your array.

When you create the gamesDictionary it is created with an retain count of +1. You then retain it (count becomes +2). When you get the value outside this function you retain again (count becomes +3).

You are correct that if you create an object you are responsible for it's memory management. Also, when you get an object from a method, you should retain it if you want to keep it around for longer than the span of the function. In your case, you just want to get at some of the properties of the object, so you don't need to retain it.

Here is a suggestion:

- (NSDictionary *)getGamesList
{
    NSMutableDictionary *gamesDictionary = [NSMutableDictionary dictionary]; // Remove the retain.
    while (sqlite3_step(statement) == SQLITE_ROW)
    {
        NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
        NSArray *gameDate = [key componentsSeparatedByString:@" "];
        NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
        NSString *date_time = [NSString stringWithFormat:@"%@, %@",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
        if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
        [[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
        [_id release];
    }
    sqlite3_reset(statement);
    return gamesDictionary; 
}

This next bit is messy. you create a new dictionary and retain it. The original dictionary is not autoreleased, so the count isn't decremented and it always hangs around. Just assign the dictionary rather than create a new one.

NSMutableDictionary *gamesDictionary = [[appDelegate getGamesList] retain];
// Retaining it, becuase it looks like it's used elsewhere.

Now, in this method:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    NSString *returnString;
    // Don't need to retain the keys because you are only using it within the function
    // and since you didn't alloc, copy or retain the array it contains, you aren't responsible for it's memory management.
    NSArray *keys = [NSArray arrayWithArray:[gamesDictionary allKeys]];
    if ([keys count] != 0) {
        returnString = [[NSString alloc] initWithString:[[keys objectAtIndex:section] uppercaseString]];
        return [returnString autorelease];
    }
    return @"";
}
Abizern
YES! It fixed the problem! Thanks a lot, you've made me happy :)All is correct, except that return [gamesDictionary autorelease]; in this construction lead to an EXC_BAD_ACCESS error. Just used return gamesDictionary instead and all became clear!Thanks again!
lonlywolf
Yes, my mistake. You didn't 'create' the gamesDictionary, so you aren't responsible for its memory management, so you don't need to call `autorelease` it.
Abizern
It seems that it was just a top of an iceberg.I open a new view in which the getGamesList is called (memory leaks says no error), and then I return to parent view and get a lot of leaks. These are: NSCFArray in if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];andNSCFString in NSString *date_time = [NSString stringWithFormat:@"%@, %@",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];and NSCFNumber in NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];:)
lonlywolf
Also the NSCFDictionary, but I solved this by addingreturn [[gamesDictionary copy] autorelease];
lonlywolf
The problem was in viewWillDisappear method. It was the wrong order of [self viewWillDisappear] and [gamesDictionary release] :)Thanks again.
lonlywolf