效果圖附上:
<br />
插入數(shù)據(jù)
<br />
刪除數(shù)據(jù)
<br />
更新數(shù)據(jù)
<br />
查找數(shù)據(jù)
<br />
<br />
分頁(yè)檢索數(shù)據(jù)
<br />
Other數(shù)據(jù)- 這里時(shí)間限制翘狱,不演示全部
-
前言
在上一篇簡(jiǎn)單的介紹了SQLite的基本使用之后,現(xiàn)在我們開(kāi)始在代碼中去完成對(duì)數(shù)據(jù)庫(kù)的操作砰苍。
內(nèi)容點(diǎn):
PS:使用C函數(shù)完成盒蟆,不使用第三方框架
- 集成SQLite
- 使用SQLite完成增刪改查
- 子線程操作數(shù)據(jù)庫(kù)
- 數(shù)據(jù)庫(kù)的優(yōu)化
<br />
-
1. 集成
-
1.1先創(chuàng)建一個(gè)Swift 3.0工程,名字隨意师骗,創(chuàng)建完成后點(diǎn)擊項(xiàng)目工程名
<br />
-
1.2創(chuàng)建一個(gè)橋接文件
command + n
<br />
-
1.3導(dǎo)入頭文件
輸入 #import <sqlite3.h>
<br />
-
1.4 然后在
ViewController
中輸入sqlite3
历等,有提示表示集成成功.
<br />
在開(kāi)始之前先說(shuō)明一下:由于代碼的注釋都寫(xiě)的很詳細(xì)了,所以不會(huì)逐行解釋量窘。
<br />
-
2. 使用SQLite完成增刪改查
要想完成對(duì)數(shù)據(jù)的增刪改查遗契,得有一個(gè)文件吧,和上一篇類(lèi)似寡夹,我們應(yīng)該先創(chuàng)建數(shù)據(jù)庫(kù)文件,然后創(chuàng)建表厂置,才能進(jìn)行增刪改查菩掏,所以我們第一步是創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)文件、然后是創(chuàng)建表昵济。然后我們創(chuàng)建一個(gè)單例類(lèi)來(lái)專(zhuān)門(mén)管理數(shù)據(jù)庫(kù)智绸。
-
2.1 創(chuàng)建單例類(lèi)
使用 command + n
創(chuàng)建一個(gè)類(lèi)SQLManager
,實(shí)現(xiàn)單例
/// 單例
static let manager : SQLManager = SQLManager()
class func shareInstance() -> SQLManager{
return manager
}
<br />
-
2.2寫(xiě)一個(gè)方法創(chuàng)建數(shù)據(jù)庫(kù)文件
創(chuàng)建數(shù)據(jù)庫(kù)文件,首先需要一個(gè)文件名访忿,然后通過(guò)文件名得到地址瞧栗,再通過(guò)地址去創(chuàng)建數(shù)據(jù)庫(kù)文件
/// 打開(kāi)數(shù)據(jù)庫(kù)
func openDB(DBName: String){
//1、拿到數(shù)據(jù)庫(kù)路徑
let path = DBName.documentDir()
//打印路徑海铆,以便拿到數(shù)據(jù)文件
print(path)
//2迹恐、轉(zhuǎn)化為c字符串
let cPath = path.cString(using: String.Encoding.utf8)
/*
參數(shù)一:c字符串,文件路徑
參數(shù)二:OpaquePointer 一個(gè)數(shù)據(jù)庫(kù)對(duì)象的地址
注意Open方法的特性:如果指定的文件路徑已有對(duì)應(yīng)的數(shù)據(jù)庫(kù)文件會(huì)直接打開(kāi)卧斟,如果沒(méi)有則會(huì)創(chuàng)建在打開(kāi)
使用Sqlite_OK判斷
sqlite3_open(cPath, &dbBase)
*/
/*
#define SQLITE_OK 0 /* Successful result */
*/
if sqlite3_open(cPath, &dbBase) != SQLITE_OK{
print("數(shù)據(jù)庫(kù)打開(kāi)失敗")
return
}
}
<br />
-
2.3如果你運(yùn)行沒(méi)有打印錯(cuò)誤提示殴边,那么就是創(chuàng)建成功了,這個(gè)時(shí)候我們就創(chuàng)建一個(gè)表珍语,表的設(shè)計(jì)如下:
有了 表的設(shè)計(jì)锤岸,我們就可以編寫(xiě)SQL語(yǔ)句了,寫(xiě)一個(gè)方法去完成表的創(chuàng)建廊酣,在創(chuàng)建表之前有一點(diǎn)我覺(jué)得有必要提一下:
在SQLite中除查詢能耻,其他的都是使用
exec
去執(zhí)行,所以先把exec
封裝一下
func execSQL(sql : String) -> Bool {
// 1、先把OC字符串轉(zhuǎn)化為C字符串
let cSQL = sql.cString(using: String.Encoding.utf8)
// 2晓猛、執(zhí)行語(yǔ)句
/// 在SQLite3中饿幅,除了查詢以外(創(chuàng)建/刪除/更新)都是用同一個(gè)函數(shù)
/*
1. 已經(jīng)打開(kāi)的數(shù)據(jù)庫(kù)對(duì)象
2. 需要執(zhí)行的SQL語(yǔ)句,c字符串
3. 執(zhí)行SQL語(yǔ)句之后的回調(diào)戒职,一般寫(xiě)nil
4. 是第三個(gè)參數(shù)的第一個(gè)參數(shù)栗恩,一般傳nil
5. 錯(cuò)誤信息,一般傳nil
SQLITE_API int SQLITE_STDCALL sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
*/
if sqlite3_exec(dbBase, cSQL, nil, nil, nil) != SQLITE_OK {
return false
}
return true
}
創(chuàng)建表
@discardableResult func createTab() -> Bool{
//1洪燥、編寫(xiě)SQL語(yǔ)句
let sql = "CREATE TABLE IF NOT EXISTS T_Person\n" +
"(\n" +
"id INTEGER PRIMARY KEY AUTOINCREMENT,\n" +
"name TEXT NOT NULL,\n" +
"age INTEGER, \n" +
"money REAL DEFAULT 100.0\n" +
");"
print(sql)
let flag = execSQL(sql: sql)
if !flag {
print("創(chuàng)建表失敗")
}
return flag
}
這里要注明一下磕秤,為什么SQL語(yǔ)句是這樣子組成的,看了打印信息就明白了
CREATE TABLE IF NOT EXISTS T_Person
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER,
money REAL DEFAULT 100.0
);
<br />
-
2.4 增
剛才我們?cè)O(shè)計(jì)了一個(gè)表包括id捧韵、name市咆、age、money再来,然后我們要進(jìn)行插入數(shù)據(jù)蒙兰,但是如果所有的數(shù)據(jù)都用單例來(lái)管理,如果我有5個(gè)表芒篷,并且每個(gè)月都有增刪改查搜变,那么我的單例會(huì)很臃腫,秉著睡的事情誰(shuí)干的原則针炉,這個(gè)時(shí)候我們?cè)趧?chuàng)建一個(gè)類(lèi):Person
,這個(gè)類(lèi)自己去管理增刪改查挠他。
/// 插入
func insertSQL() -> Bool{
//斷言
assert(name != nil, "姓名不能為空")
//1、編寫(xiě)SQL語(yǔ)句
//如果插入的是可選的篡帕,那么你會(huì)發(fā)現(xiàn)你插入的前面有一個(gè)Optional殖侵,這是很尷尬的事情,
//可以通過(guò) 赂苗! 解決
var sql : String = ""
if id == -1 {
sql = "INSERT INTO T_Person (name,age,money) VALUES('\(name!)',\(age == -1 ? 0 : age),\(money == -1 ? 100.0 : money));"
}
else{
sql = "INSERT INTO T_Person (id,name,age,money) VALUES(\(id),'\(name!)',\(age == -1 ? 0 : age),\(money == -1 ? 100.0 : money));"
}
//3愉耙、執(zhí)行語(yǔ)句
return SQLManager.shareInstance().execSQL(sql: sql)
}
<br />
-
2.5 刪
/// 刪除
func deleteSQL() -> Bool{
//1、編寫(xiě)SQL語(yǔ)句
let sql = sqlWithType(sql: "DELETE FROM T_Person", contactStr: "AND")
print("del - \(sql)")
let flag = SQLManager.shareInstance().execSQL(sql: sql)
return false
}
<br />
-
2.6 改
/// 更新
func updateSQL() -> Bool{
//斷言
assert(name != nil, "姓名不能為空")
//1拌滋、編寫(xiě)SQL語(yǔ)句
let sql = "UPDATE T_Person \n" +
"SET name='\(name!)',age=\(age),money=\(money) \n" +
"WHERE id=\(id);";
//3、執(zhí)行語(yǔ)句
return SQLManager.shareInstance().execSQL(sql: sql)
}
<br />
-
2.7 查
class func selectSQL(sql : String) -> [Person]{
//2猜谚、獲取查詢的數(shù)據(jù)
let dicts = SQLManager.shareInstance().selectSQL(sql: sql)
//3败砂、創(chuàng)建一個(gè)數(shù)組用于保存模型
var datas = [Person]()
//4、遍歷字典數(shù)組魏铅,生成模型數(shù)組
for dict in dicts{
datas.append(Person(dict: dict))
}
//5昌犹、返回模型數(shù)組
return datas
}
<br />
3、完成界面搭建:
項(xiàng)目結(jié)構(gòu)圖
作為主入口ViewController
包括了一個(gè)Toolbar
览芳、一個(gè)TableView
斜姥、一個(gè)右按鈕
。
其中toolBar
又連接著增、刪铸敏、改缚忧、查、排序杈笔、分頁(yè)等界面的入口闪水,在其他中則是一些數(shù)據(jù)庫(kù)的優(yōu)化(插入大量數(shù)據(jù)的時(shí)候如何優(yōu)化)。
<br />
3.1 增
界面包括四個(gè)
textfiled
,一個(gè)button
,一個(gè)label
,一個(gè)navigation item
- textfiled用來(lái)給用戶輸入數(shù)據(jù)
- button 用來(lái)執(zhí)行插入操作
- label用來(lái)顯示插入狀態(tài)
- navigation item用于快速插入數(shù)據(jù)蒙具,便于測(cè)試
插入按鈕代碼如下:
這里需要注意的是:類(lèi)型的判斷球榆,ID,age必須是Int類(lèi)型禁筏,name 必須是String持钉、而money必須是浮點(diǎn)型 Double,所以在取值的時(shí)候需要進(jìn)行判斷
@IBAction func insertData(_ sender: UIButton) {
//名字為空 不能插入
if nameTextfield.text?.characters.count == 0 {
status.text = statuText + "姓名不能為空"
return
}
//通過(guò)textField創(chuàng)建對(duì)象
var dict = [String : Any]()
//如果id輸入了篱昔,就插入ID每强,否則自增長(zhǎng)
if idTextfield.text?.characters.count != 0{
if idTextfield.text!.isAllNum() { //判斷是不是純數(shù)字
dict["id"] = (idTextfield.text! as NSString).intValue
}
}
//名字直接添加
dict["name"] = nameTextfield.text!
//如果輸入了年齡并且是純數(shù)字才添加
if ageTextfield.text?.characters.count != 0 {
if ageTextfield.text!.isAllNum() { //判斷是不是純數(shù)字
dict["age"] = (ageTextfield.text! as NSString).intValue
}
}
if moneyTextfield.text?.characters.count != 0 {
let double : Double = (moneyTextfield.text! as NSString).doubleValue
if double > 0{
dict["money"] = double
}
}
let p = Person(dict: dict)
if !p.insertSQL() {
status.text = statuText + "插入失敗"
}else{
status.text = statuText + "插入成功"
}
}
<br />
3.2 刪
界面包括四個(gè)
textfiled
,一個(gè)button
,一個(gè)label
,
- textfiled用來(lái)給用戶輸入數(shù)據(jù)
- button 用來(lái)執(zhí)行插入操作
- label用來(lái)顯示插入狀態(tài)
刪除按鈕代碼如下:
這里需要注意的是條件的拼接、當(dāng)沒(méi)有輸入條件的時(shí)候默認(rèn)把整個(gè)表所有數(shù)據(jù)刪除旱爆,這里我們自定義了一個(gè)字典舀射,前面是一個(gè)結(jié)構(gòu)體,后面是一個(gè)可選的值怀伦,因?yàn)橛脩艨赡茌斎?/code>
@IBAction func deleteData(_ sender: UIButton) {
var dict = [Person_Property : Any?]()
dict[.id] = idTextfield.text
dict[.name] = nameTextfield.text
dict[.age] = ageTextfield.text
dict[.money] = moneyTextfield.text
let p = Person(myDict : dict)
if p.deleteSQL(){
statuLabel.text = statuText + "刪除成功"
}
else{
statuLabel.text = statuText + "刪除失敗"
}
}
構(gòu)造方法如下:
init(myDict : [Person_Property : Any?]){
super.init()
var dict = [String : Any]()
for (key,value) in myDict {
if (value as! String).characters.count > 0 {
if key.rawValue as String == "id" {
if (value as! String).isAllNum() { //并且全是數(shù)字
dict[key.rawValue as String] = Int((value as! NSString).intValue)
}
}else if key.rawValue as String == "name" {
dict[key.rawValue as String] = value
}else if key.rawValue as String == "age" {
if (value as! String).isAllNum() { // 并且全是數(shù)字
dict[key.rawValue as String] = Int((value as! NSString).intValue)
}
}else if key.rawValue as String == "money" { //
if (value as! String).isFloatValue() { //并且是浮點(diǎn)型
dict[key.rawValue as String] = (value as! NSString).doubleValue
}
}
}
}
setValuesForKeys(dict)
}
<br />
3.3 改
界面包括一個(gè)現(xiàn)實(shí)所有數(shù)據(jù)的
tableView
和一個(gè)二級(jí)頁(yè)面脆烟,點(diǎn)擊cell進(jìn)入二級(jí)頁(yè)面,修改特定的數(shù)據(jù)
這里我并沒(méi)有把ID也列進(jìn)來(lái)房待,因?yàn)楫?dāng)數(shù)據(jù)量達(dá)到一定量的時(shí)候如果你貿(mào)然去修改ID邢羔,并且這個(gè)ID是主鍵,
很有可能會(huì)造成主鍵沖突桑孩,所以ID就一般原則而言要唯一拜鹤,且不修改
這里包括一個(gè)關(guān)閉按鈕、界面采用present的方式顯示流椒,一個(gè)當(dāng)前用戶信息的label以及三個(gè)提供給用戶輸入的textfield敏簿,一個(gè)button以及一個(gè)更新?tīng)顟B(tài)的顯示
<br />
3.4 查 、3.5 排序
由于這兩個(gè)非常類(lèi)似宣虾,界面布局都一致惯裕,所以就都拿到一起了
包括一個(gè)label
、一個(gè)textView
绣硝、一個(gè)button
蜻势、一個(gè)tableView
<br />
查找Code
class func queryPersons(condition : String) -> [Person]{
//如果輸入的為空鹉胖,就全部加載
if condition == ""{
return loadPersons()
}
//1握玛、編寫(xiě)SQL語(yǔ)句
let sql = "SELECT * FROM T_Person WHERE \(condition);"
return selectSQL(sql: sql)
}
排序Code
/// 排序
///
/// - Parameter sort: 字段
/// - Returns: 數(shù)組
class func querySortPersons(sort : String) -> [Person]{
//如果輸入的為空够傍,就全部加載
if sort == "" {
return loadPersons()
}
//1、編寫(xiě)SQL語(yǔ)句
let sql = "SELECT * FROM T_Person ORDER BY \(sort);"
return selectSQL(sql: sql)
}
<br />
3.6 分頁(yè)檢索數(shù)據(jù)
這個(gè)界面也很簡(jiǎn)單挠铲,主要包括一個(gè)
textField
冕屯、三個(gè)Button
、一個(gè)TableView
- textField 用于輸入每頁(yè)顯示的條目數(shù)
- 三個(gè)按鈕分別作用于:開(kāi)始所有市殷、上/下一頁(yè)
- tableView主要用于顯示數(shù)據(jù)
<br />
檢索代碼愕撰,通過(guò)傳入的m,n進(jìn)行搜索醋寝,如果還不是很清楚的可以去我的上一篇簡(jiǎn)書(shū)文章查看搞挣。
class func queryLimitPerson(m : Int32, n : Int32) -> [Person]{
if n == 0 {
return loadPersons()
}
let sql = "SELECT * FROM T_Person LIMIT \(m),\(n)"
return selectSQL(sql: sql)
}
三個(gè)按鈕code
private func limitDatas(m : Int32, n:Int32){
let pdatas = Person.queryLimitPerson(m : m , n: n)
if pdatas.count == 0{
showErrorText()
return
}
datas = pdatas
}
@IBAction func startLimitBtn(_ sender: UIButton) {
index = 0
limitDatas(m: Int32(index) * (textField.text! as NSString).intValue, n: (textField.text! as NSString).intValue)
}
@IBAction func nextBtn(_ sender: Any) {
index += 1
limitDatas(m: Int32(index) * (textField.text! as NSString).intValue, n: (textField.text! as NSString).intValue)
}
@IBAction func preBtn(_ sender: Any) {
index = (index - 1) < 0 ? 0 : (index - 1)
limitDatas(m: Int32(index) * (textField.text! as NSString).intValue, n: (textField.text! as NSString).intValue)
}
4、事務(wù)音羞,預(yù)編譯囱桨、線程安全
4.1 事務(wù):
事務(wù)(Transaction)是一個(gè)對(duì)數(shù)據(jù)庫(kù)執(zhí)行工作單元。事務(wù)(Transaction)是以邏輯順序完成的工作單位或序列嗅绰,可以是由用戶手動(dòng)操作完成舍肠,也可以是由某種數(shù)據(jù)庫(kù)程序自動(dòng)完成。
事務(wù)(Transaction)是指一個(gè)或多個(gè)更改數(shù)據(jù)庫(kù)的擴(kuò)展窘面。例如翠语,如果您正在創(chuàng)建一個(gè)記錄或者更新一個(gè)記錄或者從表中刪除一個(gè)記錄,那么您正在該表上執(zhí)行事務(wù)财边。重要的是要控制事務(wù)以確保數(shù)據(jù)的完整性和處理數(shù)據(jù)庫(kù)錯(cuò)誤肌括。
使用下面的命令來(lái)控制事務(wù):
BEGIN TRANSACTION:開(kāi)始事務(wù)處理。
COMMIT:保存更改酣难,或者可以使用 END TRANSACTION 命令谍夭。
ROLLBACK:回滾所做的更改。
事務(wù)控制命令只與 DML 命令 INSERT憨募、UPDATE 和 DELETE 一起使用紧索。他們不能在創(chuàng)建表或刪除表時(shí)使用,因?yàn)檫@些操作在數(shù)據(jù)庫(kù)中是自動(dòng)提交的菜谣。
<br />
- 然后我們?cè)赟QLManager中添加事務(wù)代碼
/// 開(kāi)啟事務(wù)
func beginTransaction(){
execSQL(sql: "BEGIN TRANSACTION")
}
/// 提交事務(wù)
func commitTransaction(){
execSQL(sql: "COMMIT TRANSACTION")
}
/// 回滾
func rollbackTransaction(){
execSQL(sql: "ROLLBACK TRANSACTION")
}
<br />
4.2 預(yù)編譯
關(guān)于預(yù)編譯的意思珠漂,網(wǎng)上很多種,我這里打一個(gè)比喻:
你是一個(gè)司機(jī)尾膊,今天下午要去倉(cāng)庫(kù)A里面去那2000件貨物甘磨。
預(yù)編譯:你打電話給倉(cāng)庫(kù),讓倉(cāng)庫(kù)管理員給你準(zhǔn)備好貨物眯停,然后你準(zhǔn)時(shí)到達(dá),裝貨走人卿泽。
不是預(yù)編譯:你下午三點(diǎn)到了倉(cāng)庫(kù)莺债,再讓倉(cāng)庫(kù)管理員給你準(zhǔn)備貨物滋觉,清點(diǎn)完成后再裝貨,在走人齐邦。
所以預(yù)編譯是可以提高效率的椎侠。
預(yù)編譯執(zhí)行語(yǔ)句:
@discardableResult func batchExecSQL(sql : String , args: CVarArg...) -> Bool{
//1轉(zhuǎn)化為C字符串
let cSql = sql.cString(using: String.Encoding.utf8)!
//2措拇、執(zhí)行預(yù)編譯
var stmt : OpaquePointer? = nil
if sqlite3_prepare_v2(dbBase, cSql, -1, &stmt, nil) != SQLITE_OK {
print("預(yù)編譯失敗")
sqlite3_finalize(stmt)
return false
}
//3我纪、進(jìn)行數(shù)據(jù)綁定
/*
這里要注意,下標(biāo)從1開(kāi)始丐吓。
*/
var index : Int32 = 1
/*
sqlite3_bind_XX(句柄, 下標(biāo)(從1開(kāi)始), 值)
*/
for objc in args{
if objc is Int{
sqlite3_bind_int64(stmt, index, sqlite3_int64(objc as! Int))
}else if objc is Double{
sqlite3_bind_double(stmt, index, objc as! Double)
}else if objc is String{
//得到字符串
let text = objc as! String
//得到C字符串
let cText = text.cString(using: String.Encoding.utf8)!
/*
sqlite3_bind_text(句柄, 下標(biāo), 字符串, 字符串長(zhǎng)度 -1 表示系統(tǒng)自己計(jì)算, OC傳入nil浅悉,SWIFT不行)
1 句柄
2 下標(biāo)
3 C字符串
4 C字符串長(zhǎng)度 -1 自動(dòng)計(jì)算
5 OC 傳入nil 但是SWIFT不行,因?yàn)閷?duì)象提前釋放掉了券犁,會(huì)導(dǎo)致插入的數(shù)據(jù)不對(duì)
typedef void (*sqlite3_destructor_type)(void*);
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
#define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
第五個(gè)參數(shù)如果傳入SQLITE_STATIC/nil, 那么系統(tǒng)不會(huì)保存需要綁定的數(shù)據(jù), 如果需要綁定的數(shù)據(jù)提前釋放了, 那么系統(tǒng)就隨便綁定一個(gè)值
第五個(gè)參數(shù)如果傳入SQLITE_TRANSIENT, 那么系統(tǒng)會(huì)對(duì)需要綁定的值進(jìn)行一次copy, 直到綁定成功之后再釋放
但是Swift中并不能直接寫(xiě) SQLITE_TRANSIENT 或者 -1术健,需要自定義一個(gè)SQLITE_TRANSIENT,來(lái)覆蓋系統(tǒng)的
在 124 行中
*/
sqlite3_bind_text(stmt, index, cText, -1, SQLITE_TRANSIENT)
}
index += 1
}
//4粘衬、執(zhí)行SQL語(yǔ)句
if sqlite3_step(stmt) != SQLITE_DONE {
print("執(zhí)行SQL語(yǔ)句失敗")
return false
}
//5荞估、重置STMT
if sqlite3_reset(stmt) != SQLITE_OK{
print("重置句柄失敗")
return false
}
//6、關(guān)閉STMT
sqlite3_finalize(stmt)
return true
}
<br />
4.2 線程安全
在多線程中操作數(shù)據(jù)庫(kù)是有安全隱患的稚新,可能會(huì)發(fā)生這里在執(zhí)行插入勘伺、另外又在執(zhí)行刪除、更新或者其他的指令褂删,所以多線程操作數(shù)據(jù)庫(kù)一定要保證線程安全飞醉。
如何保證呢?
- 通過(guò)創(chuàng)建一個(gè)創(chuàng)行隊(duì)列
多線程執(zhí)行操作模式
// MARK - Child Thread
/*
1 一個(gè)唯一的對(duì)列名
2 優(yōu)先級(jí)
3 隊(duì)列類(lèi)型
4
*/
private let dbQueue = DispatchQueue(label: "com.codepgq.github", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
//DispatchQueue(label:"com.appcoda.queue2", qos:DispatchQoS.userInitiated)
func execQueueSQL(action : @escaping (_ manager : SQLManager) ->()){
//開(kāi)一個(gè)子線程
DispatchQueue.global().async {
action(self)
}
}
或者通過(guò)這種方式創(chuàng)建串行隊(duì)列
// 創(chuàng)建一個(gè)串行隊(duì)列
fileprivate let dbQueue = DispatchQueue(label: "com.codepgq.github", attributes: [])
4.4OK笤妙,了解了上面的知識(shí)冒掌,我們就搭建最后一個(gè)頁(yè)面:
界面很簡(jiǎn)單,就不做介紹了蹲盘,主要是通過(guò)判斷打開(kāi)了哪些switch來(lái)進(jìn)行芳芳的選擇股毫。
<br />
所有的方法如下:
@IBAction func startInsert(_ sender: Any) {
if isSerting {
showErrorText(message : "正在插入")
return
}
isSerting = true
//計(jì)算值
let value : Int8 = isOnValue(sw: openTrans) * 100 + 10 * isOnValue(sw: openThread) + isOnValue(sw: openPrepare)
print(NSString.init(format: "value - %03d", value))
/*
001 010 100 110 101 011 000 111
*/
switch value {
case 001:
//開(kāi)啟了預(yù)編譯
insertDatas(true)
case 010:
//開(kāi)啟了子線程
openChildThread()
case 100:
//開(kāi)啟了事務(wù)
openTransaction()
case 011:
//開(kāi)啟了子線程 預(yù)編譯
openTheadAndPrepare()
case 110:
//開(kāi)啟了事務(wù) 子線程
openTransAndTheard()
case 101:
//開(kāi)啟了事務(wù) 預(yù)編譯
openTransAndPrepare()
case 111:
//開(kāi)啟了事務(wù) 子線程 預(yù)編譯
openAll()
default:
//啥都沒(méi)開(kāi)
insertDatas(false)
}
}
/// 開(kāi)啟事務(wù)和預(yù)編譯
func openTransAndPrepare(){
SQLManager.shareInstance().beginTransaction()
insertDatas(true)
SQLManager.shareInstance().commitTransaction()
}
/// 開(kāi)啟事務(wù)和線程
func openTransAndTheard(){
SQLManager.shareInstance().execQueueSQL { (manager) in
self.insertDatas(false)
}
}
//開(kāi)啟線程和預(yù)編譯
func openTheadAndPrepare(){
SQLManager.shareInstance().execQueueSQL { (manager) in
self.insertDatas(true)
}
}
//全部打開(kāi)
func openAll(){
SQLManager.shareInstance().execQueueSQL { (manager) in
manager.beginTransaction()
self.insertDatas(true)
manager.commitTransaction()
}
}
//開(kāi)啟子線程
func openChildThread(){
SQLManager.shareInstance().execQueueSQL { (manager) in
self.insertDatas(false)
}
}
//開(kāi)啟事務(wù)
func openTransaction(){
//獲取數(shù)據(jù)庫(kù)對(duì)象
let manager = SQLManager.shareInstance()
//開(kāi)始事務(wù)
manager.beginTransaction()
//插入數(shù)據(jù)
insertDatas(false)
//提交事務(wù)
manager.commitTransaction()
}
//插入數(shù)據(jù)
private func insertDatas(_ prepare : Bool) {
//得到開(kāi)始時(shí)間
let start = CFAbsoluteTimeGetCurrent()
startLabel.text = "開(kāi)始時(shí)間:\(start)"
print(#function,"\(prepare ? "預(yù)編譯" : "未開(kāi)啟預(yù)編譯" )")
//開(kāi)始插入
for index in 0..<insertCount {
let name = "rand\(index)"
let age = Int(arc4random() % 100 + 1)
let money = Double(arc4random() % 10000) + Double(arc4random() % 100) * 0.01
if prepare {
//預(yù)編譯
let sql = "INSERT INTO T_Person (name,age,money) VALUES(?,?,?);"
SQLManager.shareInstance().batchExecsql(sql, args: name,age,money)
}
else{
//直接插入
let sql = "INSERT INTO T_Person (name,age,money) VALUES('\(name)',\(age),\(money));"
SQLManager.shareInstance().execSQL(sql: sql)
}
}
//得到結(jié)束時(shí)間
let end = CFAbsoluteTimeGetCurrent()
endLabel.text = "結(jié)束時(shí)間:\(end)"
//得出耗時(shí)
timeLabel.text = "耗時(shí):\(end - start)"
isSerting = false
}
//計(jì)算當(dāng)前是不是開(kāi)啟狀態(tài)
private func isOnValue(sw : UISwitch) -> Int8{
return sw.isOn ? 1 : 0
}
<br />
什么都沒(méi)開(kāi)啟的情況下插入10000條數(shù)據(jù):
<br />
開(kāi)啟了預(yù)編譯的情況下插入10000條數(shù)據(jù):
<br />
開(kāi)啟了事務(wù)的情況下插入10000條數(shù)據(jù):
<br />
開(kāi)啟了預(yù)編譯和事務(wù)的情況下插入10000條數(shù)據(jù):
開(kāi)啟了線程的時(shí)候就不會(huì)阻塞UI,可自行測(cè)試召衔。
Demo傳送門(mén)
寫(xiě)的不好見(jiàn)諒铃诬,但是如果對(duì)你有幫助,那么我就心滿意足了苍凛。
碼字不易趣席、喜歡點(diǎn)一下。