天天看點

FMDB的使用

FMDatabaseQueue 隊列和線程安全

在多線程中同時使用 FMDatabase 單例是極其錯誤的想法,會導緻每個線程建立一個 FMDatabase 對象。不要跨線程使用單例,也不要同時跨多線程,不然會奔潰或者異常。

是以不要執行個體化一個 FMDatabase 單例來跨線程使用。

相反,使用 FMDatabaseQueue,下面就是它的使用方法:

第一,建立隊列。

// 建立 FMdatabaseQueue 示例
FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:aPath];                

然後這樣使用:

// 示例
[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:1]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:2]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:3]];

    FMResultSet *rs = [db executeQuery:@"select * from foo"];
    while ([rs next]) {
        ...
    }
}];                

把操作放在事務中也很簡單,比如:

// 示例
[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:]];
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:]];

    if (whoopsSomethingWrongHappened) {
        *rollback = YES;
        return;
    }
    // ...
    [db executeUpdate:@"INSERT INTO myTable VALUES (?)", [NSNumber numberWithInt:]];
}];
           

FMDatabase 将塊代碼 block 運作在一個串行隊列上,即使在多線程同時調用 FMDatabaseQueue 的方法,它們仍然還是順序執行。這種查詢和更新方式不會影響其它,是線程安全的。

基于 block 自定義 SQLite 函數

這是可以的,例子可以在 main.m 中的

makeFunctionNamed:

方法檢視。

Swift

在 Swift 項目中也可以使用 FMDB,需要做以下步驟:

  1. 将 FMDB 的

    .m

    .h

    全部檔案拖進你的項目。
  2. 如果 Xcode 提示建立橋接檔案,需要點選建立。如果沒有提示,且項目中也沒有橋接檔案,需要手動添加。點此檢視橋接檔案更多資訊
  3. 在橋接檔案中,添加這行代碼:

    #import "FMDB.h"

  4. 可以從 "src/extra/Swift Extension" 檔案夾中拷貝

    FMDatabaseVariadic.swift

    檔案到項目中,就可以使用

    executeUpdate

    executeQuery

    多參數了。

做完上述幾步,就可以使用 FMDatabase 寫 Swift 代碼了。

// 示例
let documentsFolder = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[] as String
let path = documentsFolder.stringByAppendingPathComponent("test.sqlite")

let database = FMDatabase(path: path)

if !database.open() {
    println("Unable to open database")
    return
}

if !database.executeUpdate("create table test(x text, y text, z text)", withArgumentsInArray: nil) {
    println("create table failed: \(database.lastErrorMessage())")
}

if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["a", "b", "c"]) {
    println("insert 1 table failed: \(database.lastErrorMessage())")
}

if !database.executeUpdate("insert into test (x, y, z) values (?, ?, ?)", withArgumentsInArray: ["e", "f", "g"]) {
    println("insert 2 table failed: \(database.lastErrorMessage())")
}

if let rs = database.executeQuery("select x, y, z from test", withArgumentsInArray: nil) {
    while rs.next() {
        let x = rs.stringForColumn("x")
        let y = rs.stringForColumn("y")
        let z = rs.stringForColumn("z")
        println("x = \(x); y = \(y); z = \(z)")
    }
} else {
    println("select failed: \(database.lastErrorMessage())")
}

database.close()