iOS内存中的Sqlite问题

时间:2011-10-14 02:54:33

标签: ios memory memory-management sqlite

我花了好几个小时试图解决这个问题,但我只是放弃了;我不知道出了什么问题。

在我的应用程序中,我执行嵌套SQL操作以正确设置所有对象。出于某种原因,有时sqlite3对象无法正常释放,导致内存上升。我理解正确使用sql3_close和sql3_finalize是一个问题。但是,正如您将看到的,我认为我已正确使用它们。

以下是问题的根源:

- (NSArray *) getAllSessions {
    if (sqlite3_open(dbPath, &db) == SQLITE_OK) { 
        if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
            while (sqlite3_step(statement) == SQLITE_ROW) {

                //I found out that doing something like that
                //toAdd.in_loc = [self getIndoorLocationWithId:[NSNumber numberWithInt:(int)sqlite3_column_int(statement, 6)]];
                //messes the memory up all the time
                //but doing that works OK:
                NSNumber *_id = [[NSNumber alloc]  initWithInt:(int) sqlite3_column_int(statement, 5)];
                toAdd.out_loc = [self getOutdoorLocationWithId:_id];
                [_id release];

                //So I did the same with the second one, but this one messes memory up:
                NSNumber *id2 = [[NSNumber alloc] initWithInt:(int)sqlite3_column_int(statement, 6)];
                toAdd.in_loc = [self getIndoorLocationWithId:id2];
                [id2 release];
            }
            sqlite3_finalize(statement);
        }
        sqlite3_close(db);
    } 
}

所以这是一个混乱记忆的人:

- (IndoorLocation *) getIndoorLocationWithId:(NSNumber *) locId {
    if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) { 
        if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
            while (sqlite3_step(statement) == SQLITE_ROW) {  
                //if I comment the thing below it works
                NSNumber *_id = [[NSNumber alloc]  initWithInt:(int) sqlite3_column_int(statement, 5)];
                toReturn.outLoc = [self getOutdoorLocationWithId:_id];
                [_id release];

            }
            sqlite3_finalize(statement);
        }
        sqlite3_close(db);
    }
}

所以在那个混淆内存的那个中,我使用与第一次完全相同的函数(getOutdoorLocationwithId),但是它不起作用,sqlite3对象不能正常释放。

我希望你理解我的问题,这让我疯了!

2 个答案:

答案 0 :(得分:3)

在这样的单个用户应用中,无需持续打开和关闭。这与基于服务器的应用程序不同,在该应用程序中,您与多个用户建立连接池,其中保持连接可能会破坏池并损害可伸缩性。在手机应用程序中打开它并保持打开状态。你完成后关闭它。至少,在递归调用中执行此操作。

更糟糕的是,你在递归调用中打开和关闭 - 只需将其打开。

此外:

  • 您没有查看finalize的返回代码
  • 您没有查看关闭的返回代码。
  • 您正在准备(编译)这些声明,但您没有将它们保存起来 - 在已保存的声明中调用重置并再次执行。

考虑使用FMDB - 它是一个很好的包装器。

顺便说一句,这里有一个更丰富,更持久的关闭,但不要在每次通话时都使用它。当你结束或者你的应用程序进入后台时关闭它...这是我的,类似于fmdb所做的。

- (void)close
{
    if (_sqlite3)
    {
        ENInfo(@"closing");
        [self clearStatementCache];

        int rc = sqlite3_close(_sqlite3);
        ENDebug(@"close rc=%d", rc);

        if (rc == SQLITE_BUSY) 
        { 
            ENError(@"SQLITE_BUSY: not all statements cleanly finalized");

            sqlite3_stmt *stmt; 
            while ((stmt = sqlite3_next_stmt(_sqlite3, 0x00)) != 0) 
            {
                ENDebug(@"finalizing stmt");
                sqlite3_finalize(stmt); 
            }

            rc = sqlite3_close(_sqlite3);
        }

        if (rc != SQLITE_OK)
        {
            ENError(@"close not OK.  rc=%d", rc);
        }

        _sqlite3 = NULL;
    }
}

答案 1 :(得分:0)

我遇到了同样的问题。 当我使用并打开FMDB时,它可以正常工作。但在其他回调中它失败并抛出了“关闭泄露的声明”的例外。最后我发现这是因为我保留了[FMDatabase databaseWithPath:dbPath]的指针,指针是一个自动释放对象。我解决了这个问题:

FMDatabase *db = [[FMDatabase databaseWithPath:dbPath] retain];

当您想要关闭数据库时:     db [关闭];     [db release];     db = nil;

通过这种方式,您并不总是为每个操作打开和关闭数据库,应该是一个管理它的管理器对象。当经理启动时,数据库始终打开,直到经理停止。