理一下 iOS 本地持久化儲(chǔ)存(側(cè)重?cái)?shù)據(jù)庫(kù)SQLite

理一下 iOS 本地持久化儲(chǔ)存(側(cè)重?cái)?shù)據(jù)庫(kù)SQLite

來(lái)源: Toyun(@陳思Siming)

鏈接:http://www.reibang.com/p/10a26d01dc84

公司的項(xiàng)目存在已有兩年腋粥,版本也到三點(diǎn)幾了勺届,但是本地持久化數(shù)據(jù)存儲(chǔ)魂莫,始終用的是GVUserDefaults這個(gè)對(duì)NSUserDefaults進(jìn)行了擴(kuò)展的第三方庫(kù)。但隨著業(yè)務(wù)的發(fā)展,需要存儲(chǔ)的地方越來(lái)越多,GVUserDefaults也越來(lái)也不能適應(yīng)需求,當(dāng)我們都忍受不了的時(shí)候扭倾,經(jīng)過(guò)一番商討之后,決定使用FMDB這個(gè)封裝了SQLite3的第三方庫(kù)挽绩。

此篇文章以此為主線膛壹,理一理數(shù)據(jù)庫(kù)和本地化儲(chǔ)存的一些知識(shí),但是此篇文章絲毫沒(méi)有提到CoreData唉堪,喜歡使用CoreData的同學(xué)模聋,這里先說(shuō)聲對(duì)不起了浪費(fèi)了您20s寶貴的時(shí)間。

此篇文章的邏輯如下圖所示:

iOS本地持久化儲(chǔ)存方式概述

說(shuō)起iOS本地化儲(chǔ)存的方式巨坊,大家估計(jì)在也熟悉不過(guò)了撬槽,NSUserDefault、File趾撵,Keychain侄柔、DataBase無(wú)非也就這幾種方式共啃。

NSUserDefault、File:這兩種使用方式都很簡(jiǎn)單暂题,需要注意的一點(diǎn)就是所存儲(chǔ)的對(duì)象都需要遵守并實(shí)現(xiàn)NSCoding協(xié)議中的兩個(gè)方法移剪,適用的范圍也都是一些小規(guī)模數(shù)據(jù),其實(shí)NSUserDefault的底層實(shí)現(xiàn)還是以.plist文件進(jìn)行儲(chǔ)存的薪者。

Keychain:用于儲(chǔ)存一些私密信息纵苛,比如密碼、證書(shū)等等,Keychain里保存的信息不會(huì)因App被刪除而丟失言津,在用戶重新安裝App后依然有效攻人。同樣也適用于應(yīng)用之間數(shù)據(jù)共享。

DataBase:說(shuō)到儲(chǔ)存悬槽,就不能不提DataBase技術(shù)怀吻,移動(dòng)端所用的DBMS一般都是SQLite3,在iOS下初婆,SQLite3的API是純C語(yǔ)言的蓬坡,這樣我們一直以來(lái)面向?qū)ο箝_(kāi)發(fā)的朋友們,突然找不到了對(duì)象磅叛,有點(diǎn)那么的驚慌失措屑咳。好在開(kāi)源社區(qū)出現(xiàn)了像FMDB這樣優(yōu)秀的第三方框架幫我們找回了對(duì)象,同樣蘋果自己也出了新的技術(shù)就是所謂的CoreData(但CoreData并不是此篇文章介紹的重點(diǎn))弊琴。數(shù)據(jù)庫(kù)適合儲(chǔ)存大規(guī)模數(shù)據(jù)训枢,而且查找起來(lái)也比上述方式方便道伟,所以大量?jī)?chǔ)存的時(shí)候操软,還是要?jiǎng)佑脭?shù)據(jù)庫(kù)遗嗽,這也是我們放棄GVUserDefaults的原因。

數(shù)據(jù)庫(kù)的基本知識(shí)

數(shù)據(jù)庫(kù)

首先要有數(shù)據(jù)Data臣缀,然后數(shù)據(jù)多了名字就升級(jí)了稱為數(shù)據(jù)庫(kù)DataBase,這個(gè)時(shí)候就需要有管理系統(tǒng)去管理數(shù)據(jù)庫(kù)也就是DataBaseManagerSystem泻帮,最后佐以DataBaseAdministrator及上述名稱成為了一個(gè)系統(tǒng)就是所謂的DataBaseSystem精置。

Data –> DataBase –> DataBaseManagerSystem –> DataBaseSystem

Data: 數(shù)據(jù)庫(kù)存儲(chǔ)的基本對(duì)象。

DataBase: 說(shuō)起來(lái)數(shù)據(jù)庫(kù)锣杂,大家都有一個(gè)模模糊糊的概念脂倦,不就是一個(gè)存放數(shù)據(jù)的大倉(cāng)庫(kù)嗎,這還有什么好說(shuō)的元莫。其實(shí)這樣理解就已經(jīng)很好了赖阻,但是說(shuō)的更專業(yè)一點(diǎn)就是數(shù)據(jù)庫(kù)是一個(gè)以某種有組織的方式存儲(chǔ)的數(shù)據(jù)集合。

DataBaseManagerSystem: 數(shù)據(jù)庫(kù)管理系統(tǒng)是位于用戶與操作系統(tǒng)之間的一層數(shù)據(jù)管理軟件踱蠢。常見(jiàn)的DBMS有MySQL火欧、PostgreSQL棋电、 Microsoft SQL Server、Oracle苇侵、Sybase赶盔、IBM DB2。當(dāng)然這些都是用于服務(wù)端的DBMS榆浓,移動(dòng)端用的DBMS是SQLite3于未,這也是本文講述的重點(diǎn)。

DataBaseSystem: 在計(jì)算機(jī)系統(tǒng)中引入數(shù)據(jù)庫(kù)后的系統(tǒng)陡鹃,一般由數(shù)據(jù)庫(kù)烘浦,數(shù)據(jù)庫(kù)管理系統(tǒng),應(yīng)用系統(tǒng)萍鲸,數(shù)據(jù)庫(kù)管理成員(DBA)構(gòu)成闷叉。一般情況下稱數(shù)據(jù)庫(kù)系統(tǒng)為數(shù)據(jù)庫(kù),所以有時(shí)候我們就會(huì)感到迷糊猿推,你說(shuō)DBS片习,他以為DB,你轉(zhuǎn)到DB上了蹬叭,他又開(kāi)始講DBS藕咏。所以真正想理解透一些知識(shí)的時(shí)候,基本概念一定要清晰一些秽五,這樣才不容易迷糊孽查。

SQL語(yǔ)句

SQL(Structured Query Language),結(jié)構(gòu)化查詢語(yǔ)言坦喘,專門用來(lái)與數(shù)據(jù)庫(kù)通信的語(yǔ)言盲再。既然要操作數(shù)據(jù)庫(kù),SQL語(yǔ)句是必須要會(huì)寫(xiě)的瓣铣。下面我簡(jiǎn)單列舉幾條簡(jiǎn)單的SQL語(yǔ)句僅供參考答朋。想要學(xué)習(xí)更多的SQL語(yǔ)句的知識(shí),請(qǐng)查閱其他資料棠笑,本篇在這兒不過(guò)多敘述梦碗。

這里以學(xué)生表為例寫(xiě)幾條簡(jiǎn)單的SQL語(yǔ)句

// 創(chuàng)表(table)一張學(xué)生表 表名:student

? 字段id: ?學(xué)生編號(hào),作為主鍵蓖救,類型為整形

? 字段name:學(xué)生名字洪规,類型為字符串,并且不能為空值

? 字段age: 學(xué)生年齡循捺,類型為整形可為空斩例。

? 其中字段又稱之為列(column)

create table if not exists student (

? ?id integer primary key autoincrement,

name text not null,

age integer);

// 刪除學(xué)生表

drop table if exists student;

// 插入一條記錄,主鍵id會(huì)自增長(zhǎng)自動(dòng)賦值从橘,

其中記錄又稱之為行(row)

insert into student (name, age) values ('小明', 20);

// 刪除名字為小明的學(xué)生念赶,

這里的關(guān)鍵字where就是條件础钠,如果無(wú)此條件,則影響整張表

delete from student where name = '小明';

// 更新符合條件的記錄

update student set age = 21 where name = '小明';

// 查詢符合條件的記錄

select * from student where name = '小明';

數(shù)據(jù)庫(kù)的使用

如果你沒(méi)有使用CoreData晶乔,那么無(wú)論你使用的是純C語(yǔ)言的SQLite3庫(kù)珍坊,還是使用對(duì)其進(jìn)行封裝了的FMDB,你都要設(shè)計(jì)出適合自己業(yè)務(wù)的表結(jié)構(gòu)正罢。關(guān)于表的設(shè)計(jì)阵漏,可以有兩種設(shè)計(jì)模式。

數(shù)據(jù)庫(kù)表的設(shè)計(jì)

第一種設(shè)計(jì)方式

以模型中的每個(gè)屬性作為表的一個(gè)字段翻具,這樣在查詢履怯、讀取放面操作起來(lái)更為方便,但是我個(gè)人感覺(jué)這種模式適合的業(yè)務(wù)是記錄條數(shù)不多裆泳。而且字段盡可能的不要更改叹洲。創(chuàng)建好表之后,在去修改表結(jié)構(gòu)還是聽(tīng)麻煩的一件事的工禾。具體使用运提,可參考下面的代碼:

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, assign) NSUInteger age;

@end

// 數(shù)據(jù)儲(chǔ)存的路徑

NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *dbpath = [document stringByAppendingPathComponent:@"toyun.sqlite"];

// 鏈接數(shù)據(jù)庫(kù)

self.db = [FMDatabase databaseWithPath:dbpath];

// 打開(kāi)數(shù)據(jù)庫(kù)

if ([self.db open]) {

[self.db executeUpdate:@"create table if not exists student ( id integer primary key autoincrement, name text not null, age integer not null)"];

}

// 實(shí)例化對(duì)象,即要儲(chǔ)存的對(duì)象

NSMutableArray *array = [NSMutableArray array];

for (NSInteger i = 0; i

第二種設(shè)計(jì)方式

這一種設(shè)計(jì)模式是將model作為一個(gè)字段直接將model轉(zhuǎn)為NSData儲(chǔ)在此字段中闻葵,在這里要指出的是model必須實(shí)現(xiàn)NSCoding協(xié)議民泵,不過(guò)實(shí)際項(xiàng)目中我們字典轉(zhuǎn)模型大都是使用第三方庫(kù),而現(xiàn)在比較流行的三個(gè)字典轉(zhuǎn)模型的第三方庫(kù)Mantle槽畔、MJExtension栈妆、YYModel都默認(rèn)對(duì)此進(jìn)行了處理,所以這兒稍微注意一下就好厢钧。第二種方法更適合于記錄條數(shù)比較多鳞尔,和業(yè)務(wù)的相關(guān)性不是太敏感,而且如果有查找排序這樣的需求的話早直,可以從model挑出某些屬性作為表中附帶的一些字段寥假。具體使用,參考下面的代碼:

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, assign) NSUInteger age;

@end

@implementation Student

- (void)encodeWithCoder:(NSCoder *)aCoder {

[aCoder encodeObject:_name forKey:@"name"];

[aCoder encodeInteger:_age forKey:@"age"];

}

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {

if (self = [super init]) {

_name = [aDecoder decodeObjectForKey:@"name"];

_age = [aDecoder decodeIntegerForKey:@"age"];

}

return self;

}

@end

NSString *document = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *dbpath = [document stringByAppendingPathComponent:@"yeemiao.sqlite"];

self.db = [FMDatabase databaseWithPath:dbpath];

if ([self.db open]) {

[self.db executeUpdate:@"create table if not exists student (id integer primary key autoincrement, model blob)"];

}

NSMutableArray *array = [NSMutableArray array];

for (NSInteger i = 0; i

數(shù)據(jù)庫(kù)使用中線程安全

在多線程中操作數(shù)據(jù)庫(kù)霞扬,我們就要考慮線程安全問(wèn)題昧旨,不然就有可能引起數(shù)據(jù)錯(cuò)亂的問(wèn)題。解決辦法有很多祥得,可以自己去加鎖,但是讀寫(xiě)速度要求高的話就不太建議加鎖了蒋得。SQLite3對(duì)于多線程是直接支持的级及,SQLite3庫(kù)提供了三種方式:single thread、multi thread额衙、serialized饮焦。同樣 FMDB自己也提供了多線程操作數(shù)據(jù)庫(kù)的類FMDatabaseQueue怕吴,這個(gè)使用起來(lái)還是比較簡(jiǎn)單的。

刪除緩存

現(xiàn)在大多數(shù)的App都是有本地化儲(chǔ)存的县踢,但是卻不是每個(gè)App都對(duì)應(yīng)有刪除緩存的功能转绷,起碼我們自己的App是沒(méi)有做刪除緩存這個(gè)功能的,緩存是為了節(jié)省流量硼啤,刪除緩存是為了節(jié)省空間我認(rèn)為這兩個(gè)功能同等重要议经,但是這個(gè)需求提了幾次,產(chǎn)品不怎么重視扒捶怠煞肾!特別是在16G的版本下,這個(gè)功能還能能提升一些用戶體驗(yàn)的嗓袱。

此功能到也不難實(shí)現(xiàn)籍救,搞清楚各種儲(chǔ)存方式他們把文件存儲(chǔ)到了那個(gè)文件夾下面,不同的數(shù)據(jù)我們儲(chǔ)存的地方明顯也是不同的渠抹,然后根據(jù)需求刪除對(duì)應(yīng)的數(shù)據(jù)就OK了蝙昙,沙盒的具體目錄如下所示:

// 沙盒下的文件夾目錄

Documents: iTunes會(huì)同步此文件夾,不應(yīng)該刪除

Library

? ?Caches: 緩存文件夾,刪除緩存主要?jiǎng)h除的文件夾

? ?Preferences: NSUserDefault寫(xiě)入此文件夾下梧却,iTunes會(huì)同步奇颠,不應(yīng)該刪除

tmp:系統(tǒng)創(chuàng)建的臨時(shí)文件夾,隨時(shí)有可能被刪除

做清除緩存功能時(shí)篮幢,可把Library/Caches文件夾下面的文件全部刪除大刊,也可以根據(jù)業(yè)務(wù)的需要?jiǎng)h除指定的文件夾。

總結(jié)

此篇文章還是比較偏重于原理三椿,對(duì)于具體的使用則沒(méi)有說(shuō)太多缺菌,具體使用各種各樣,但是基本原理是比較統(tǒng)一的搜锰。關(guān)于數(shù)據(jù)庫(kù)CURD由于我們不是做服務(wù)端開(kāi)發(fā)伴郁,所以也沒(méi)有深究,想要研究的話蛋叼,可在查閱其他資料焊傅。除了上述所說(shuō)的,可能在具體使用中還要考慮數(shù)據(jù)庫(kù)版本遷移狈涮,數(shù)據(jù)庫(kù)同步等需求狐胎。這些由于我的水平所限,也沒(méi)有提到可以參考我寫(xiě)此文時(shí)的一篇參考博文《iOS應(yīng)用架構(gòu)談(4):本地持久化方案及動(dòng)態(tài)部署》歌馍。而NSUserDefault握巢、File使用方法都很簡(jiǎn)單也都沒(méi)有介紹。CoreData太過(guò)于重量級(jí)松却,我在項(xiàng)目中也沒(méi)事實(shí)際運(yùn)用過(guò)暴浦,這里也沒(méi)有介紹溅话。

第二種設(shè)計(jì)的方式挺適用,對(duì)于表較多并且各個(gè)表字段不一歌焦,建議使用表名與實(shí)體類映射飞几,通過(guò)runtime獲取所有實(shí)體類屬性進(jìn)行增刪改查等操作!


?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末独撇,一起剝皮案震驚了整個(gè)濱河市屑墨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌券勺,老刑警劉巖绪钥,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異关炼,居然都是意外死亡程腹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門儒拂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)寸潦,“玉大人,你說(shuō)我怎么就攤上這事社痛〖” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蒜哀,是天一觀的道長(zhǎng)斩箫。 經(jīng)常有香客問(wèn)我,道長(zhǎng)撵儿,這世上最難降的妖魔是什么乘客? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮淀歇,結(jié)果婚禮上易核,老公的妹妹穿的比我還像新娘。我一直安慰自己浪默,他們只是感情好牡直,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著纳决,像睡著了一般碰逸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阔加,一...
    開(kāi)封第一講書(shū)人閱讀 50,096評(píng)論 1 291
  • 那天花竞,我揣著相機(jī)與錄音,去河邊找鬼。 笑死约急,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的苗分。 我是一名探鬼主播厌蔽,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼摔癣!你這毒婦竟也來(lái)了奴饮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤择浊,失蹤者是張志新(化名)和其女友劉穎戴卜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體琢岩,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡投剥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了担孔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片江锨。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖糕篇,靈堂內(nèi)的尸體忽然破棺而出啄育,到底是詐尸還是另有隱情,我是刑警寧澤拌消,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布挑豌,位于F島的核電站,受9級(jí)特大地震影響墩崩,放射性物質(zhì)發(fā)生泄漏氓英。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一泰鸡、第九天 我趴在偏房一處隱蔽的房頂上張望债蓝。 院中可真熱鬧,春花似錦盛龄、人聲如沸饰迹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)啊鸭。三九已至,卻和暖如春匿值,著一層夾襖步出監(jiān)牢的瞬間赠制,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钟些,地道東北人烟号。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像政恍,于是被迫代替她去往敵國(guó)和親汪拥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容