我花了好几个小时试图解决这个问题,但我只是放弃了;我不知道出了什么问题。
在我的应用程序中,我执行嵌套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对象不能正常释放。
我希望你理解我的问题,这让我疯了!
答案 0 :(得分:3)
在这样的单个用户应用中,无需持续打开和关闭。这与基于服务器的应用程序不同,在该应用程序中,您与多个用户建立连接池,其中保持连接可能会破坏池并损害可伸缩性。在手机应用程序中打开它并保持打开状态。你完成后关闭它。至少,在递归调用中执行此操作。
更糟糕的是,你在递归调用中打开和关闭 - 只需将其打开。
此外:
考虑使用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;
通过这种方式,您并不总是为每个操作打开和关闭数据库,应该是一个管理它的管理器对象。当经理启动时,数据库始终打开,直到经理停止。