FMDB ResultSet 总是 returns 只有一行

FMDB ResultSet always returns only one row

我正在尝试在我的一个项目中使用 sqlite 数据库。

工作正常;但出于某种原因,发生了一些事情,我找不到那个错误。

resultSet 对象总是在第一条记录之后退出。 该数组始终只有 1 条记录。(可能由于错误而离开了 while)

我创建了一个 DBManager class,这个 DBManager class 包含不同的内部 类。我有一个私有的全局 FMDatabase 实例(我在使用它之前在某处对其进行了初始化)

如您所见,有 2 个不同的打印错误行

当我运行时,第二个打印行给出了这个错误:

调用sqlite3_step时出错(21:内存不足)rs 错误域=FMDatabase 代码=7 "out of memory" UserInfo=0x790308d0 {NSLocalizedDescription=内存不足}

本应包含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:table 之间的唯一区别是(据我所知)"id" 列。此元素 table 有一个 "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,但另一个没有任何 id 列。但是他们两个都用了很长时间。

“内存不足”错误是误导性的 SQLite 错误,这意味着 SQLite 函数是用 NULL 值调用的 sqlite3*指针。在 FMDB 中,这意味着您关闭了数据库,但随后尝试继续使用相同的 FMDatabase 实例(无需再次调用 open)。

现在,我没有看到您在此代码示例中这样做,但此代码示例采用了一些可能导致此类错误的做法。也就是说,您不是在本地实例化 FMDatabase 对象,而是使用一些 database 属性 并且您 运行 有其他函数可能使用它的风险(可能是 [= DataElement 的 16=] 方法?也许您为了简洁起见而删除了其他一些功能?也许是其他线程?)。

让我们想象一下,这个函数调用了一些其他的函数来再次打开数据库(FMDB 会悄悄地让你这样做,如果数据库已经打开,基本上 return 会立即打开),执行一些 SQL,然后关闭数据库,那么这个例程,当它去检索第二行信息的时候,会发现数据库关闭了,导致你描述的错误。数据库对象的打开和关闭不是递归的。一旦子程序关闭它,它就完全关闭了。

现在所有这些都是假设的,因为没有看到您的其余代码的作用,我无法确认。但它的场景适合您共享的代码以及您描述的症状。

假设确实如此,这里有两种可能的解决方案:

  1. 您可以简单地打开数据库一次并保持打开状态,直到应用程序终止。这可能是最简单的方法,并且可能比您在此处使用的方法更有效。 SQLite 在提交单个 SQL 语句(或事务)方面实际上非常稳健,因此人们通常让数据库处于打开状态。

    我什至可能更进一步,并建议如果你可能有不同的线程与这个数据库交互,你实例化一个单一的 FMDatabaseQueue 对象,并在整个应用程序中使用它,同样不要打开和关闭每时每刻。请注意,如果您使用 FMDatabaseQueue,您需要更加明智地确保位于 inDatabaseinTransaction 块中间的一个函数不会调用另一个尝试执行另一个 inDatabaseinTransaction 块的函数(否则你会死锁)。但与任何共享资源一样,您希望对资源的锁定位置和释放时间保持敏感,数据库也不例外。

  2. 如果您决定像此代码示例建议的那样在每个函数中打开和关闭数据库(同样,我不建议这样做),那么不要使用 class 属性 跟踪数据库。当然,有一些打开数据库的函数,但是 return 这个 FMDatabase 对象,每个函数都有它自己的本地实例,并在完成时关闭该实例。

    但您真的想避免一个函数关闭数据库影响其他函数行为的意外后果。

我也遇到了同样的问题,因为从 table 中删除记录后我关闭了连接,一段时间后我又调用了一个 sql 查询,在那个时间点我遇到了同样的问题

"FMDatabase Code=7 "内存不足" UserInfo=0x790308d0 {NSLocalizedDescription=内存不足}"

只需在关闭连接之前添加一行代码,即

database = nil database.close()

稍后在模态 class 中添加方法,如下所示

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
}

它解决了我的问题。