FMDB ResultSet всегда возвращает только одну строку

Я пытаюсь использовать базу данных sqlite в одном из своих проектов.

Он работал нормально; но по какой-то причине что-то случилось, и я не смог найти эту ошибку.

Объект resultSet всегда завершается после первой записи. В массиве всегда только 1 запись. (возможно, оставила на время из-за ошибки)

Я создал класс DBManager, и этот класс DBManager содержит разные внутренние классы. У меня есть частный глобальный экземпляр FMDatabase (и я где-то инициализирую его перед использованием)

Как видите, есть 2 разные строки ошибок печати

Когда я бегу, вторая строка печати выдает эту ошибку:

Ошибка при вызове sqlite3_step (21: недостаточно памяти) rs Error Domain = FMDatabase Code = 7 «недостаточно памяти» UserInfo = 0x790308d0 {NSLocalizedDescription = out of memory}

А в массиве, который должен содержать более 300 записей, всего 1 запись. (Последняя строка печати всегда 1)

Эта часть выглядит простой. (У меня где-то еще есть совершенно аналогичный код, но он отлично работает)

private var database : FMDatabase!

class DBManager{

    class Element{

        class func get()->[DataElement]{
            database.open()
            println( database.lastError() )

            var result = [DataElement]()

            var resultSet: FMResultSet! = database!.executeQuery("SELECT * FROM Element WHERE working = 1", withArgumentsInArray: nil)

            while resultSet.next(  ) {
                let data = DataElement( 
                    id : Int(resultSet.intForColumn("id")),
                    name: resultSet.stringForColumn("name"), 
                    server: resultSet.stringForColumn("server"), 
                    working: resultSet.boolForColumn("working") )
                result.append( data )
            }

            println( database.lastError() )
            database.close()

            println( result.count )
            return result
        }
    }
}

PS: Единственная разница между этими таблицами (насколько я понимаю) - это столбец «id». Эта таблица элементов имеет "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, но другая таблица не имеет столбца id. Но оба они долгое время хорошо работали.


person Rephisto    schedule 09.04.2015    source источник


Ответы (2)


Ошибка нехватки памяти - это вводящая в заблуждение ошибка SQLite, которая означает, что функция SQLite была вызвана со значением NULL для указателя sqlite3*. В FMDB это означает, что вы закрыли базу данных, но затем попытались продолжить использование того же экземпляра FMDatabase (без повторного вызова open).

Я не вижу, чтобы вы делали это в этом примере кода, но в этом образце кода используются некоторые методы, которые делают возможными такого рода ошибки. А именно, вместо того, чтобы создавать экземпляр объекта FMDatabase локально, вы используете какое-то свойство database и рискуете, что какая-то другая функция могла его использовать (может быть, метод init для DataElement? Может быть, какая-то другая функция, которую вы удалили для краткости? может какая-нить другая?).

Давайте представим, что эта функция вызвала какую-то другую функцию, которая снова открыла базу данных (что FMDB позволит вам сделать это, в основном немедленно возвращая, если база данных уже открыта), выполняет некоторый SQL, а затем закрывает базу данных, а затем эту процедуру, когда она пошел, чтобы получить вторую строку информации, обнаружил бы, что база данных закрыта, что привело бы к описанной вами ошибке. Открытие и закрытие объекта базы данных не рекурсивно. Как только подпрограмма закрыла его, он полностью закрылся.

Все это гипотетически, потому что, не видя того, что делает остальная часть вашего кода, я не могу подтвердить. Но это сценарий, который соответствует тому, какой код вы поделились, в сочетании с описанными вами симптомами.

Предполагая, что это действительно так, здесь есть два возможных решения:

  1. Вы можете просто открыть базу данных один раз и оставить ее открытой до завершения работы приложения. Это, вероятно, самый простой подход и, вероятно, более эффективен, чем тот, что у вас здесь. SQLite на самом деле довольно надежен с точки зрения фиксации отдельных операторов SQL (или транзакций), поэтому люди обычно оставляют базу данных открытой.

    Я мог бы даже пойти дальше и предложить, чтобы, если у вас могут быть разные потоки, взаимодействующие с этой базой данных, вы создаете экземпляр одного объекта FMDatabaseQueue и используете его во всем приложении, опять же, не открывая и не закрываясь все время. Обратите внимание: если вы используете FMDatabaseQueue, вам нужно быть еще более осмотрительным в отношении того, чтобы одна функция, находящаяся в середине блока inDatabase или inTransaction, не вызывала другую функцию, которая пытается выполнить другой блок inDatabase или inTransaction (или иначе вы попадете в тупик). Но, как и любой общий ресурс, вы хотите быть внимательными к тому, где ресурс заблокирован и когда он освобожден, и базы данных не являются исключением из этого правила.

  2. Если вы полны решимости открывать и закрывать базу данных в каждой функции, как предлагает этот пример кода (опять же, практика, которую я бы не советовал), то не используйте свойство класса для отслеживания базы данных. Конечно, у вас есть функция, которая открывает базу данных, но возвращает этот объект FMDatabase, и каждая функция будет иметь собственный локальный экземпляр и закроет этот экземпляр, когда с ним будет работать.

    Но вы действительно хотите избежать непредвиденных последствий закрытия базы данных одной функцией, влияющих на поведение другой функции.

person Rob    schedule 10.04.2015
comment
Первый помог мне решить мою проблему, я открываю базу только один раз. Я не мог найти точную причину. Вероятно, как вы сказали, за это отвечает другой поток. Спасибо. - person Rephisto; 22.04.2015

Я также столкнулся с той же проблемой, так как после удаления записей из таблицы я закрываю соединение, и через некоторое время я вызываю еще один запрос sql, в этот момент у меня была такая же проблема

"FMDatabase Code = 7" не хватает памяти "UserInfo = 0x790308d0 {NSLocalizedDescription = out of memory}"

просто добавьте одну строку кода перед закрытием соединения, т. е.

database = nil database.close()

Позже добавьте метод в свой модальный класс, как показано ниже.

func openDatabase() -> Bool {
    if database == nil {            
        if !FileManager.default.fileExists(atPath: pathToDatabase) {                
            let documentsDirectory = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString) as String
            pathToDatabase = documentsDirectory.appending("/\(databaseFileName)")
            print(pathToDatabase)              
           }
         database = FMDatabase(path: pathToDatabase)
    }           
    if !database.isOpen {
        database.open()
    }
    return true
}

Перед выполнением любого запроса проверьте это,

if openDatabase() {
//Query
}

Это решило мою проблему.

person Arshad Shaik    schedule 02.12.2017