在iOS内存问题的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对象不能正确释放。

我希望你明白我的问题,这是让我疯狂!

在这样的单个用户应用程序中,不需要持续打开和closures。 这不像一个基于服务器的应用程序,你有多个用户的连接池,持有连接可以击败池并伤害可伸缩性。 在电话应用程序中打开并保持打开状态。 完成后closures它。 至less,在recursion调用中做到这一点。

更糟糕的是,你正在recursion调用中打开和closures,只是把它打开。

也:

  • 你不检查finalize的返回码
  • 你不检查close的返回码。
  • 您正在准备(编译)这些语句,但是您并没有将它们保存起来 – 在保存的语句中调用reset并重新执行。

考虑使用FMDB – 这是一个很好的包装。

顺便说一句,这里是一个更丰富和更持久的closures,但不要在每次通话中使用它。 当你结束或者你的应用程序进入后台时closures它…这是我的,它类似于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; } } 

我遇到了同样的问题。 当我使用并打开FMDB时,它工作正常。 但在其他callback失败,并抛出“封闭泄漏声明”的例外。 最后我发现这是因为我保留了[FMDatabase databaseWithPath:dbPath]的指针,指针是一个autorelease对象。 我通过这个解决了这个问题:

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

当你想closures数据库时:db [close]; [db释放]; db = nil;

由此,你不总是打开和closures每个操作的数据库,应该是一个经理对象会计。 当经理启动时,数据库一直打开,直到经理停止。