我正试图在我的一个项目中使用sqlite数据库。
它运行良好;但出于某种原因,发生了一些事情,我找不到那个bug。
resultSet对象总是从第一条记录之后退出。数组中总是只有一条记录。(可能是因为错误而离开了一段时间)
我创建了一个DBManager类,这个DBManager类包含不同的内部类。我有一个私有的全局FMDatabase实例(在使用它之前我会在某个地方初始化它)
如您所见,有两条不同的打印错误线
当我运行时,第二行打印会出现以下错误:
调用sqlite3_step时出错(21:内存不足)rs错误域=FM数据库代码=7"内存不足"UserInfo=0x790308d0{NSLocalizedDescription=内存不足}
数组应该包含300多条记录,但其中只有一条记录。(最后一行总是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"列。此Element表有一个"id"INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,但另一个表没有任何id列。但在很长一段时间里,他们俩都表现得很好。
;内存不足";error是一个误导性的SQLite错误,这意味着用sqlite3*
指针的NULL
值调用了SQLite函数。在FMDB中,这意味着您关闭了数据库,但随后尝试继续使用相同的FMDatabase
实例(不再次调用open
)。
现在,我在这个代码示例中没有看到您这样做,但这个代码示例采用了一些实践,使这种错误成为可能。也就是说,您不是在本地实例化FMDatabase
对象,而是在使用一些database
属性,并且您可能会面临其他函数可能已经使用它的风险(可能是DataElement
的init
方法?可能是为了简洁起见删除的其他函数?可能是其他线程?)。
让我们想象一下,这个函数调用了另一个函数,该函数再次打开数据库(FMDB会悄悄地让你这样做,如果数据库已经打开,基本上会立即返回),执行一些SQL,然后关闭数据库,然后这个例程在检索第二行信息时,会发现数据库关闭,从而导致你描述的错误。数据库对象的打开和关闭不是递归的。一旦子程序关闭它,它就完全关闭了。
现在所有这些都是假设的,因为如果不了解代码的其余部分,我就无法确认。但这是一个适合您共享的代码和您描述的症状相结合的场景。
假设真的是这样,这里有两种可能的解决方案:
-
您只需打开数据库一次,然后一直打开,直到应用程序终止。这可能是最简单的方法,而且可能比这里的方法更有效。SQLite在提交单个SQL语句(或事务)方面实际上非常健壮,所以人们通常会打开数据库。
我甚至可能更进一步,建议如果您可能有不同的线程与该数据库交互,那么您实例化一个
FMDatabaseQueue
对象,并在整个应用程序中使用它,同样不要总是打开和关闭。请注意,如果使用FMDatabaseQueue
,则需要更加谨慎地确保位于inDatabase
或inTransaction
块中间的一个函数不会调用试图执行另一个inDatabase
或inTransaction
块的另一个函数(否则将死锁)。但与任何共享资源一样,您希望对资源锁定的位置和释放的时间保持敏感,数据库也不例外。 -
如果你决定像这个代码示例建议的那样在每个函数中打开和关闭数据库(同样,我不建议这样做),那么不要使用类属性来跟踪数据库。当然,有一些函数可以打开数据库,但返回这个
FMDatabase
对象,每个函数都有自己的本地实例,并且在使用完该实例后会关闭该实例但是,您确实希望避免一个函数关闭数据库而影响其他函数行为的意外后果。
我也面临着同样的问题,因为从表中删除记录后,我将关闭连接,过了一段时间,我又调用了一个sql查询,当时我得到了同样的问题
"FM数据库代码=7"内存不足"UserInfo=0x790308d0{NSLocalizedDescription=内存不足}">
在关闭连接之前只需添加一行代码,即
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
}
它解决了我的问题。