0

Instrument is showing memory leak in following code. I have made sure everywhere retain count is maintained. I have also added autorelease pool, still memory leak is there. How to solve this?

Code Block:

- (NSArray *)lookupAllForSQL:(NSString *)sql
{
    sqlite3_stmt *statement;
    id result = nil;
    NSMutableArray *thisArray = [NSMutableArray arrayWithCapacity:4];
    statement = [self prepare:sql];
    if (statement)
    {
        while (sqlite3_step(statement) == SQLITE_ROW)
        {
            NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

            NSMutableDictionary *thisDict = [NSMutableDictionary dictionaryWithCapacity:4];
            for (int i = 0 ; i < sqlite3_column_count(statement) ; i++)
            {
                NSAutoreleasePool *poolInside = [[NSAutoreleasePool alloc] init];
                if(sqlite3_column_type(statement,i) == SQLITE_NULL)
                {
                    continue;
                }
                if (sqlite3_column_decltype(statement,i) != NULL &&
                    strcasecmp(sqlite3_column_decltype(statement,i),"Boolean") == 0)
                {
                    result = [NSNumber numberWithBool:(BOOL)sqlite3_column_int(statement,i)];
                }
                else if (sqlite3_column_type(statement,i) == SQLITE_INTEGER)
                {
                    result = [NSNumber numberWithInt:(int)sqlite3_column_int(statement,i)];
                }
                else if (sqlite3_column_type(statement,i) == SQLITE_FLOAT)
                {
                    result = [NSNumber numberWithFloat:(float)sqlite3_column_double(statement,i)];
                }
                else
                {
                    if((char *)sqlite3_column_text(statement,i) != NULL)
                    {
                        //result = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement,i)];
                        [thisDict setObject:[NSString stringWithUTF8String:(char *)sqlite3_column_text(statement,i)]
                                     forKey:[NSString stringWithUTF8String:sqlite3_column_name(statement,i)]];
                        //[result release];
                        result = nil;
                    }
                }
                if (result)
                {
                    [thisDict setObject:result forKey:[NSString stringWithUTF8String:sqlite3_column_name(statement,i)]];
                }
                [poolInside drain];
            }
            [thisArray addObject:[NSDictionary dictionaryWithDictionary:thisDict]];
            [pool drain];
        }
    }
    sqlite3_finalize(statement);
    return thisArray;
}

Instrument Screenshot:

enter image description here

Baby Groot
  • 4,637
  • 39
  • 52
  • 71
  • 1
    Occasionally SQLite will slip up and return a string that you have to `free()` yourself. See if that's the problem. – CodaFi May 28 '13 at 23:16
  • @CodaFi Seems you were correct. Return variable needed to be handled properly rather than variables within this function. Thanku – Baby Groot May 30 '13 at 16:51
  • You may want to throw an issue up on that guy's repo. – CodaFi May 30 '13 at 20:30
  • Hi, I edited my answer again. I really think that the "continue" was (or is) the problem. – LuisEspinoza May 30 '13 at 20:51
  • @LuisEspinoza i removed autorelease pool code from this code block. I just released all variables holding skdatabase output properly. And now instrument is not showing any leak in this function. – Baby Groot May 31 '13 at 05:40

2 Answers2

0

I think that the continue statement may be a problem. Since continue statement causes that control goes to the next interation of the closest loop, if the condition (sqlite3_column_type(statement,i) == SQLITE_NULL) is true the control will pass to next iteration and the 'poolInside' pool would not be drained. So I would use:

if(sqlite3_column_type(statement,i) == SQLITE_NULL)
{
    [poolInside drain];
    continue;
}

However, this could be an error that you added trying to solve the leak, so comment from @CodaFi is still valid.

For the sake of completeness

If you all take a look at the consequences of the poolInside leak, you can see that this line:

[thisArray addObject:[NSDictionary dictionaryWithDictionary:thisDict]];

Will leak a NSDictionary with every poolInside leak. So, this means that the returned array, will contain leaked objects. Also, I suspect that leaking a nested NSAutoreleasePool could cause that the objects created inside your for loop were leaked too. I think that the "Instruments Screenshot" that you posted shows that behavior.

LuisEspinoza
  • 8,508
  • 6
  • 35
  • 57
0

I got the solution. This function was returning an NSArray, which I was not releasing properly. So, all those variables were causing memory leak not this function.

This video helped me understand that.

Baby Groot
  • 4,637
  • 39
  • 52
  • 71