sqlite for swift不稳定

时间:2015-01-25 22:17:53

标签: ios sqlite swift

我已经设置了swift项目来使用sqlite。有时,插入时实际上并不插入正确的(或全部)值。我知道,因为我重新启动应用程序,当我回来时,条目是随机错误(没有插入的东西)或零。但有时是正确的。

这里是我设置的地方,是的,插入前的数据中的数据是正确的。

let update = "INSERT INTO ToDoItem (itemName, completed, goalDate) " + "VALUES (?, ?, ?);"
var statement: COpaquePointer = nil
if sqlite3_prepare_v2(database, update, -1, &statement, nil) == SQLITE_OK {
    let itemName = item.itemName as String
    let completed = item.completed == true ? 1 : 0

    sqlite3_bind_text(statement, 1, itemName, -1, nil)
    sqlite3_bind_int(statement, 2, Int32(completed))

    if let goalDate = item.goalDate?.toString() {
        sqlite3_bind_text(statement, 3, goalDate, -1, nil)
    } else {
        sqlite3_bind_text(statement, 3, "", -1, nil)
    }

    //println("inserting \(itemName), \(completed) and \(item.goalDate?.toString())")
    //println("")
}

if sqlite3_step(statement) != SQLITE_DONE {
    println("error updateing table")
    sqlite3_close(database)
    return
}
sqlite3_finalize(statement)

sqlite3_close(database)

你可以在中间看到注释掉的println,如果没有注释掉,那么itemName有时会得到该字符串的一部分。

3 个答案:

答案 0 :(得分:22)

我遇到了同样的问题。我找到了解决这个问题的方法。

sqlite3_bind_text(statement, 1, itemName, -1, nil) --> itemName should be UTF8 String

您应将itemName转换为NSString并使用UTF8String将字符串转换为UTF8。正确的代码在这里是相同的

let itemName = item.itemName as NSString
sqlite3_bind_text(statement, 1, itemName.UTF8String, -1, nil)
祝你好运。

答案 1 :(得分:4)

在快速的SQLite中,没有定义SQLITE_TRANSIENTSQLITE_STATIC,因此我们需要显式定义它。

交换3和4

定义SQLITE的以下属性

let SQLITE_STATIC = unsafeBitCast(0, to: sqlite3_destructor_type.self)
let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

SQL函数 绑定文本SQLITE_TRANSIENT时,添加nil而不是sqlite3_bind_text作为最后一个参数。

let update = "INSERT INTO ToDoItem (itemName, completed, goalDate) " + "VALUES (?, ?, ?);"
var statement: OpaquePointer = nil
if sqlite3_prepare_v2(database, update, -1, &statement, nil) == SQLITE_OK {
    let itemName = item.itemName as String
    let completed = item.completed == true ? 1 : 0

    sqlite3_bind_text(statement, 1, itemName, -1, SQLITE_TRANSIENT)
    sqlite3_bind_int(statement, 2, Int32(completed))

    if let goalDate = item.goalDate?.toString() {
        sqlite3_bind_text(statement, 3, goalDate, -1, SQLITE_TRANSIENT)
    } else {
        sqlite3_bind_text(statement, 3, "", -1, SQLITE_TRANSIENT)
    }

    //println("inserting \(itemName), \(completed) and \(item.goalDate?.toString())")
    //println("")
}

if sqlite3_step(statement) != SQLITE_DONE {
    println("error updateing table")
    sqlite3_close(database)
    return
}
sqlite3_finalize(statement)

sqlite3_close(database)
  

引用自SQLITE_TRANSIENT undefined in Swift

答案 2 :(得分:-2)

此行为实际上符合规范,并且代码中存在错误。

问题的根源在于您传递给String的快速sqlite3_bind_textsqlite3_bind_text接受该文字为const char*,在Swift中将其作为UnsafePointer<CChar>桥接。将Swift String传递给接受UnsafePointer<CChar>的函数时的行为记录在&#34;常量指针&#34; Interacting with C APIs的一部分。它说:

  

该字符串将在缓冲区中自动转换为UTF8,并将指向该缓冲区的指针传递给该函数。

这可能是你想要发生的事情。

但是它还说:

  

传递给函数的指针保证仅在函数调用期间有效。在函数返回后,不要试图保持指针并访问它。

这是你的bug的来源。实际上,sqlite3_bind_text()确实会将指针保留在预准备语句中,直到sqlite3_finalize()被调用,以便它可以在将来的sqlite3_step()调用中使用它。

另一个答案建议使用NSString.UTF8String。这有一个更长的寿命,似乎应该足够了。 documentation说:

  

此C字符串是指向字符串对象内部结构的指针,该结构的生命周期可能短于字符串对象,并且肯定不会有更长的生命周期。因此,如果需要将C字符串存储在使用此属性的内存上下文之外,则应复制该字符串。

&#34;记忆环境&#34;这里看起来很模糊。我不确定它是否意味着当前的功能,或者直到当前的自动释放池被耗尽,或其他什么。如果它是前两个之一,那么你是安全的。如果没有,我会说你仍然是安全的,因为似乎NSString.UTF8String已经在这样的情况下使用了很长时间没有问题..