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,需要做以下步驟:
- 将 FMDB 的
和.m
全部檔案拖進你的項目。.h
- 如果 Xcode 提示建立橋接檔案,需要點選建立。如果沒有提示,且項目中也沒有橋接檔案,需要手動添加。點此檢視橋接檔案更多資訊
- 在橋接檔案中,添加這行代碼:
#import "FMDB.h"
- 可以從 "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()