首先我關於移動端的儲存,其實有很多的解決方案,ios端>可以使phlist,UserDefaults,歸檔,當然還有很多人覺得>難用的CoreData,當然最重要的還有SQLite,SQLite設計>之初就是為了解決小型資料庫使用的,輕,便,快。所以 當然蘋果中也可以使用SQLite,所以蘋果公司就為我們封 裝了,CoreData這個api為了方便開發者的使用,但是有很多人卻覺得不怎麼好用,也可能是因為,對接觸過資料庫操作的人來說相應的查詢等語句會更加的親切好用。
這裡我就不討論CoreData了,因為最近正在研究CoreData,因為蘋果自己很多的應用使用的是CoreData,我覺得難用的東西一但會用了,就能夠事半功倍了。
不多這次就先說下SQLite--一個普遍的移動端資料持久化資料庫。
首先說下SQLite: 雖然蘋果為我們提供了CoreData但我們也可以使用比較低層的SQLite. 在swift中如何使用呢?(swift3.0) 首先也要引入庫libsqlite3.0.tbd由於swift中要使用oc先要引入標頭檔案。所以這次我就用橋接檔案直接把所有的引用放在橋接檔案.h中
這樣我們就能直接在swift使用SQLite了。在使用之前,基本的SQLite語法還是要了解的,這個只要百度,谷歌搜尋就能找到很多,我在這裡只是介紹一些比較常用的
//OpaquePointer: *db,資料庫控制程式碼,跟檔案控制程式碼FIFL類似,這裡是sqlite3指標;
//sqlite3_stmt: *stmt,相當於ODBC的Command物件,用於儲存編譯好的SQL語句;
//sqlite3_open(): 開啟資料庫,沒有資料庫時建立;
//sqlite3_exec(): 執行非查詢的SQL語句;
//sqlite3_step(): 在呼叫sqlite3_prepare後,使用這個函式在記錄集中移動;
//sqlite3_close():關閉資料庫檔案;
//sqlite3_column_text():取text型別的資料;
//sqlite3_column_blob():取blob型別的資料;
//sqlite3_column_int():取int型別的資料
複製程式碼
在OC中可以使用sqlite3_stmt*指標運算元據庫,在swift中由於基本不使用指標,但是我們可以使用OpaquePointer作為替代,OpaquePointer是swift中不透明指標,當然swift中還有UnsafePointer這種明確準確的指標可以使用,但在這裡他們不是重點。
由於大名鼎鼎的FMDB在開發者中的廣泛使用,使我們能夠減少避免使用更低層的語言來運算元據庫和使用。但是作為一名沒事找事的程式設計師還是想要使用自己的類庫,所以就稍微看了看FMDB的原始碼再結合一些資料庫的知識,給自己寫了個SQLite的Manager類,方便以後運算元據庫。
首先資料庫都是儲存在本地的沙盒裡的,我們可以給自己建立一個資料庫檔名字一般是“XXX.SQLite”當然建立一次就夠了,接著再使用之前還是需要開啟的不然就不能使用了。同時為了方便使用也寫把Manager分裝成了單例
開啟資料庫也可以使用sqlite3_open來開啟資料庫,但是有時會報錯,error: only read為了確保資料庫的可讀寫,就使用sqlite3_open_v2來開啟和配置資料庫為READWRITE(可讀寫)
資料庫已經有了,可是沒有表格也是不能儲存資料的,接下來就是建表
這裡會做一些判斷,主要是資料庫有沒有判斷還有表格已存在的判斷 現在表格有了,之後想幹嘛就能幹嘛了,哈哈哈 接著是表格的增刪改查 可以使用sqlite3_exec()來執行,但是效率會低點,因為sqlite3_exec()會反覆的執行sqlite3_prepare_v2,sqlite3_step等所以效率會低,所以我們直接使用sqlite3_prepare_v2,sqlite3_step 首先是插入操作,由於每個人的想法思路不同,程式碼結構也會不同 語句判斷(臨時寫的還需要大改)
func prepareForSQLite(type:SQLliteExecType,paramDic:[String:Any]?,tableName:String)->OpaquePointer?{
if !existsTable(name: tableName) {
debugPrint("表格不存在")
return nil
}
var stmt : OpaquePointer? = nil
guard let params = paramDic else{
return nil
}
if params.keys.count < 0{
return nil
}
// 1.獲取建立表的SQL語句
let querySQL = arrayToString(type:type,paramDic: paramDic!, tableName: tableName)
var rc:Int32? = nil
if !(stmt != nil) {
rc = sqlite3_prepare_v2(p, querySQL.codeUTF8(),-1, &stmt, nil)
if rc != SQLITE_OK {
debugPrint("新增過程錯誤")
sqlite3_finalize(stmt)
return nil
}
}
var idx = 0
let queryCount = sqlite3_bind_parameter_count(stmt);
for (key,value) in params {
let parameterName = ":\(key)"
let namedIdx = sqlite3_bind_parameter_index(stmt, parameterName.codeUTF8());
if (namedIdx > 0) {
// 在這裡繫結
self.bindObjectByIndexWithP(obj: value, ndx: namedIdx, stmt: stmt!)
idx+=1;
}
else {
debugPrint("Could not find index for \(key)")
}
}
_ = stepSQL(sql: querySQL, stmt: stmt!)
if (idx != Int(queryCount)) {
print("Error: the bind count is not correct (executeQuery)")
sqlite3_finalize(stmt)
}
return stmt
}
複製程式碼
private func arrayToString(type:SQLliteExecType,paramDic:[String:Any],tableName:String)->String{
switch type {
case .insert:
let values = paramDic.keys
let keys = paramDic.keys.joined(separator: ",")
let valuess = values.map { (str) -> String in
return ":\(str)"
}.joined(separator: ",")
let querySQL = "INSERT OR REPLACE INTO \(tableName)(\(keys)) VALUES(\(valuess));"
return querySQL
case .delete:
let querySQL = "DELETE FROM \(tableName);"
return querySQL
case .update:
return ""
default:
let querySQL = "SELECT * FROM \(tableName);"
return querySQL
}
}
複製程式碼
//MARK:<=======資料bind=======>
//MARK:繫結資料
///
/// - Parameters:
/// - obj: 資料
/// - index: 位置
/// - stmt: 指標sqlite3_stmt物件
private func bindObjectByIndexWithP(obj:Any,ndx:Int32,stmt:OpaquePointer) {
var flag:CInt = 0
if let txt = obj as? String {
flag = sqlite3_bind_text(stmt, CInt(ndx), txt, -1, SQLITE_TRANSIENT)
} else if let data = obj as? NSData {
flag = sqlite3_bind_blob(stmt, CInt(ndx), data.bytes, CInt(data.length), SQLITE_TRANSIENT)
} else if let date = obj as? Date {
let txt = "\(date.timeIntervalSince1970)"
flag = sqlite3_bind_text(stmt, CInt(ndx), txt, -1, SQLITE_TRANSIENT)
} else if let val = obj as? Bool {
let num = val ? 1 : 0
flag = sqlite3_bind_int(stmt, CInt(ndx), CInt(num))
} else if let val = obj as? Double {
flag = sqlite3_bind_double(stmt, CInt(ndx), CDouble(val))
} else if let val = obj as? Int {
flag = sqlite3_bind_int(stmt, CInt(ndx), CInt(val))
} else {
flag = sqlite3_bind_null(stmt, CInt(ndx))
}
if flag != SQLITE_OK {
sqlite3_finalize(stmt)
}
}
複製程式碼
程式碼有點亂,但是基本的思路是
sqlite3_prepare_v2 準備 sqlite3_bind_XXX 繫結資料 sqlite3_step 單步執行 sqlite3_finalize 釋放操作
只要學會了,在配合order by , limit , offfset 等的SQLite語句基本可以使適用全部的操作,這裡不再做過多的闡述,直接上程式碼(增刪改查都有,具體怎麼用還要看程式碼的習慣和工程的構造) 程式碼比較亂,其中也附帶了很多其他有用的我自己整理的寫的一些方法函式等的extension喜歡的可以用,順便點下star. github:https://github.com/taosiyu/RainSQLiteDB