sqlite для swift нестабилен

Я настроил swift project для использования 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 ответов


у меня была та же проблема. Я нашел способ решить эту проблему.

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)

удачи.


в swift SQLite нет SQLITE_TRANSIENT и SQLITE_STATIC определено, поэтому нам нужно явно определить его.

swift 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 не определено в Swift


такое поведение действительно соответствует спецификации и ошибка в коде.

источник вашей проблемы находится в swift String вы переходите к sqlite3_bind_text. sqlite3_bind_text принимает текст как const char* который соединен как UnsafePointer<CChar> в Swift. Поведение при прохождении Swift String к функции, которая принимает UnsafePointer<CChar> документируется в разделе "постоянные указатели"взаимодействие с C API. Он говорит:

в строка автоматически преобразуется в UTF8 в буфере, и указатель на этот буфер передается функции.

вероятно, что вы хотите, чтобы произошло.

но он говорит:

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

этот это источник вашей ошибки. sqlite3_bind_text() фактически сохраняется указатель в подготовленном заявлении, возможно, до sqlite3_finalize() называется так, что он может использовать его в будущем sqlite3_step() звонки.

другой ответ предложил использовать NSString.UTF8String. У этого есть жизнь, которая немного длиннее и кажется, что этого должно быть достаточно. The документация говорит:

эта строка C является указателем на структуру внутри объекта string, которая может иметь время жизни короче, чем объект string и, безусловно, не будет иметь более длительного срока службы. Поэтому следует скопировать строку C, если она должна храниться вне контекста памяти, в котором используется это свойство.

"контекст памяти" здесь кажется расплывчатым. Я не уверен, означает ли это текущую функцию, или пока текущий autoreleasepool не будет осушен, или что-то еще. Если это один из первых двух, вы в безопасности. Если нет, я бы сказал, что вы все еще в безопасности, потому что это похоже на NSString.UTF8String есть использовался в подобных ситуациях в течение длительного времени без проблем..