views:

53

answers:

2

After fixing other leaks that Instruments was indicating, I find myself in need for help to find my leaches in the following code:

-(void) readHistoryFromDatabase { sqlite3 *database;

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

NSInteger currentMonth;

int noMonths = 0;

int historyCount = 0;

NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];

[dateFormat setDateFormat:@"yyyy-MM-dd"];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:@"database.sqlite"];

if(sqlite3_open([filePath UTF8String], &database) == SQLITE_OK) { 
    const char *sqlStatement = "select data, rata, var, strftime('%m', data) month from rates where id=?"; 
    sqlite3_stmt *compiledStatement; 

    if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
        sqlite3_bind_int(compiledStatement, 1, id);
        while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
            NSDate   *data      = [dateFormat dateFromString:[NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 0)]];
            NSString *rata      = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 1)];
            NSString *var       = [NSString stringWithUTF8String:(char *) sqlite3_column_text(compiledStatement, 2)];
            NSInteger month     = sqlite3_column_int(compiledStatement, 3);

            if ((currentMonth != month) && (historyCount > 0)) {
                [self.rate setValue:[days copy] forKey:[NSString stringWithFormat:@"%d", noMonths]];

                noMonths++;
                [days removeAllObjects];
                currentMonth = month;
            } else if (historyCount == 0) {
                currentMonth = month;
            }

            CHis *new = [[CHis alloc] init]; 
            new.rata  = rata;
            new.var   = variatie;
            new.month = month;
            new.data  = data;
            historyCount++;
            [days addObject:new];
            [new release];
        }
        if ([days count] > 0) {
            [self.rate setValue:[days copy] forKey:[NSString stringWithFormat:@"%d", noMonths]];
        }
    }
    sqlite3_finalize(compiledStatement);
}
[dateFormat release];
[days release];
sqlite3_close(database);

}

self.rate is defined this way:

@property (nonatomic, retain) NSMutableDictionary *rate;

and in viewDidLoad:

self.rate = [[NSMutableDictionary alloc] init];

Basically this part of code reads from a database some rates. Based on the month it will put in a NSDictionary a pair of month and an Array with the rates.

I don't understand where is the leak. I know that perhaps it is something very easy but I am searching for 2 days now for a solution .....

A: 

In instruments you can see where the leaks is coming from, press the extended details button and select your leak, you will then see what method and file and line the leak is coming from.

Simon
+2  A: 

[[NSMutableDictionary alloc] init] returns an object with retain count 1. Then you assign it to self.rate which increases the retain count (will then be 2). If you assign something else to self.rate, the previous object's retain count will drop back to 1. But it won't reach 0.

There are three solutions:

// OK
self.rate = [[[NSMutableDictionary alloc] init] autorelease];

// Better, has the same effect as statement above
self.rate = [NSMutableDictionary dictionary];

// Clumsy, but works
NSMutableDictionary *tmp = [[NSMutableDictionary alloc] init];
self.rate = tmp;
[tmp release];

Remember, everytime you call alloc/init or copy, you get an object that you own. This means you are immediately responsible for calling release on them. Almost every other method will/should return an object that you do not need to call release on, except if you called retained on it to "claim ownership".

DarkDust
Thank you for your answer. Doesn't have any meaning that in dealloc I have putted: [rate release];?
It is correct (and necessary) to have either `[rate release]` or `self.rate = nil` (which has the same effect) in your dealloc method. But the problem as you presented it in your posting is that the retain count of "rate" will also be one too many because there is no corresponding "release" to your "init". Remember, `self.rate = ...` increases the retain count. So by the time your dealloc gets called, the retain count of the "rate" variable is 2. See this for a good explanation: http://stackoverflow.com/questions/6578/understanding-reference-counting-with-cocoa-objective-c
DarkDust