【问题标题】:Sqlite in iOS memory issuesiOS 内存问题中的 Sqlite
【发布时间】:2011-12-07 10:27:02
【问题描述】:

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

在我的应用程序中,我执行嵌套 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 对象没有被正确释放。

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

【问题讨论】:

  • 数据库已经在 getAllSessions 中打开,重新打开然后关闭会搞砸。然后在这两种方法中声明一个返回类型,但你没有返回任何东西。 xcode 不会抱怨这个吗?
  • 为了简单起见,我没有包含所有代码。关于您的第一点,我同意,但为什么它第一次有效,第二次无效?

标签: ios memory memory-management sqlite


【解决方案1】:

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

更糟糕的是,你在递归调用中打开和关闭 - 让它保持打开状态。

还有:

  • 你没有检查 finalize 的返回码
  • 您没有检查关闭的返回码。
  • 您正在准备(编译)语句,但没有将它们保存 - 对保存的语句调用 reset 并再次执行。

考虑使用 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;
    }
}

【讨论】:

  • 真棒打开和关闭一直是问题,非常感谢!
【解决方案2】:

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

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

当你想关闭数据库时: 分贝[关闭]; [数据库发布]; db = nil;

这样,您不必总是为每个操作打开和关闭数据库,应该是一个管理器对象来负责它。当管理器启动时,数据库始终处于打开状态,直到管理器停止。

【讨论】:

    猜你喜欢
    • 2016-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    • 1970-01-01
    相关资源
    最近更新 更多