Sqlite в проблемах памяти iOS - PullRequest
2 голосов
/ 14 октября 2011

Я часами пытался это исправить, но я просто сдался; Понятия не имею, что не так.

В моем приложении я выполняю вложенные операции 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 ]

3 голосов
/ 14 октября 2011

В таком однопользовательском приложении нет необходимости постоянно открывать и закрывать.Это не похоже на серверное приложение, где у вас есть пул соединений с несколькими пользователями, где удержание соединения может повредить пул и повредить масштабируемости.В приложении для телефона откройте его и держите открытым.закройте его, когда закончите.Как минимум, сделайте это в рекурсивном вызове.

Чтобы еще хуже, вы открываете и закрываете в рекурсивных вызовах - просто оставьте его открытым.

Также:

  • Вы не проверяете коды возврата finalize
  • Вы не проверяете коды возврата close.
  • Вы готовите (компилируете) статистику, но выне сохраняя их - вызовите сброс для сохраненного оператора и выполните его снова.

Рассмотрите возможность использования 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;
    }
}
0 голосов
/ 23 октября 2013

Я встречал ту же проблему. Когда я использую и открываю FMDB, все работает нормально. но в других обратных вызовах он потерпел неудачу и выдал исключение «Закрытие утечки заявления». Наконец, я обнаружил, что это потому, что я сохранил указатель из [FMDatabase databaseWithPath: dbPath], а указатель является объектом автоматического выпуска. Я исправил проблему следующим образом:

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

и когда вы хотите закрыть базу данных: дБ [закрыть]; [выпуск БД]; дБ = ноль;

Таким образом, вы не всегда открываете и закрываете базу данных для каждой операции, это должен быть объект менеджера, который ее учитывает. При запуске менеджера база данных всегда открыта, пока менеджер не остановится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...