sqlite for swift is unstable

前端 未结 3 523
谎友^
谎友^ 2020-12-10 03:14

I\'ve set up swift project to use sqlite. sometimes, when inserting it doesn\'t actually insert the correct (or all) the values. I know because I restart the app, and when

相关标签:
3条回答
  • 2020-12-10 03:34

    I had the same problem. I found the way to resolve this problem.

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

    You should convert itemName as NSString and use UTF8String to convert your string to UTF8. Right code is the same here

    let itemName = item.itemName as NSString
    sqlite3_bind_text(statement, 1, itemName.UTF8String, -1, nil)
    

    Good luck.

    0 讨论(0)
  • 2020-12-10 03:56

    This behavior actually does conform to the specification and the bug is in your code.

    The source of your problem is in the swift String you are passing to sqlite3_bind_text. sqlite3_bind_text accepts the text as a const char* which is bridged as UnsafePointer<CChar> in Swift. The behavior when passing a Swift String to a function that accepts UnsafePointer<CChar> is documented in the "Constant Pointers" section of the Interacting with C APIs. It says:

    The string will automatically be converted to UTF8 in a buffer, and a pointer to that buffer is passed to the function.

    Which is probably what you want to have happen.

    BUT it also says:

    The pointer passed to the function is guaranteed to be valid only for the duration of the function call. Don’t try to persist the pointer and access it after the function has returned.

    This is the source of your bug. sqlite3_bind_text() does in fact persist the pointer in the prepared statement possibly until sqlite3_finalize() is called so that it can use it in future sqlite3_step() calls.

    Another answer suggested using NSString.UTF8String. This has a lifetime that is a little longer and seems like it should be enough. The documentation says:

    This C string is a pointer to a structure inside the string object, which may have a lifetime shorter than the string object and will certainly not have a longer lifetime. Therefore, you should copy the C string if it needs to be stored outside of the memory context in which you use this property.

    "memory context" here seems vague. I'm not sure if it means the current function, or until the current autoreleasepool is drained, or something else. If it is one of the first two, you are safe. If not, well I would say you are still safe because it seems like NSString.UTF8String has been used in situations like these for a long time without issues..

    0 讨论(0)
  • 2020-12-10 03:57

    In swift SQLite doesn't have SQLITE_TRANSIENT and SQLITE_STATIC defined, so we need to explicitly define it.

    swift 3 & 4

    Define following property of SQLITE

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

    SQL function Add SQLITE_TRANSIENT instead of nil as last parameter while binding text 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)
    

    Referenced from SQLITE_TRANSIENT undefined in Swift

    0 讨论(0)
提交回复
热议问题