Title: iOS數(shù)據(jù)持久化
##數(shù)據(jù)持久化概念
數(shù)據(jù)持久化就是將內(nèi)存中的數(shù)據(jù)模型轉(zhuǎn)換為存儲(chǔ)模型,以及將存儲(chǔ)模型轉(zhuǎn)換為內(nèi)存中的數(shù)據(jù)模型的統(tǒng)稱。
##數(shù)據(jù)持久化的好處
1. 程序代碼重用性強(qiáng),即使更換數(shù)據(jù)庫,只需要更改配置文件,不必重寫程序代碼。
2. 業(yè)務(wù)邏輯代碼可讀性強(qiáng),在代碼中不會(huì)有大量的SQL語言,提高程序的可讀性。
3. 持久化技術(shù)可以自動(dòng)優(yōu)化,以減少對(duì)數(shù)據(jù)庫的訪問量,提高程序運(yùn)行效率。
##iOS數(shù)據(jù)持久化方案
- NSUserDefault
- 文件存儲(chǔ) (包括:Plist屬性列表、archive 歸檔、文件流)
- 鑰匙串(keychain)
- 數(shù)據(jù)庫相關(guān) (Core Data、SQLite 3)
###沙盒機(jī)制
在iOS中每個(gè)APP都擁有自己的**沙盒**,APP**只能訪問**對(duì)應(yīng)沙盒中存儲(chǔ)的數(shù)據(jù), iOS是**不允許**跨越沙盒去訪問數(shù)據(jù)的,所有的數(shù)據(jù)都是保存在該沙盒的三個(gè)子目錄下。
###沙盒目錄結(jié)構(gòu)
```
應(yīng)用程序.ipa
Documents
Library
\Caches
\Preferences
Temp
```
- `應(yīng)用程序包`: 這里存放的是應(yīng)用程序的源文件,包括資源文件和可執(zhí)行文件。
```
NSString *ApplicationPath = [[NSBundle mainBundle] bundlePath];
```
- `Document`: 一般在該目錄下保存一些比較重要的數(shù)據(jù),當(dāng)連接 iTunes后會(huì)自動(dòng)同步數(shù)據(jù)。
```
NSString *DocumentPath = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES).firstObject];
```
`PS:? 如果將數(shù)據(jù)資源保存到該目錄.上架可能會(huì)被拒絕,(解決方案:直接設(shè)置該文件夾不被iTunes備份),總之:不能保存從網(wǎng)上下載的數(shù)據(jù),否則不能上架。`
- `Library`: 存儲(chǔ)應(yīng)用設(shè)置或者狀態(tài)信息等,在該目錄下還有兩個(gè)子目錄: `Caches和Preference`
```
NSString *CachesPath =? [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject];
```
`Library/Caches`: 存放緩存文件,iTunes不會(huì)備份,因此文件不會(huì)因APP退出而刪除(一般使用SDWebImage的緩存資源都是保存到這來)
`Library/Preference`: 保存應(yīng)用的所有偏好設(shè)置,iOS的Setting(設(shè)置)會(huì)在該目錄查找該應(yīng)用的設(shè)置信息,iTunes會(huì)同步數(shù)據(jù)
- temp: 臨時(shí)文件, iTunes不會(huì)備份該文件夾中的數(shù)據(jù), 這個(gè)文件夾中的數(shù)據(jù),會(huì)因?yàn)閼?yīng)用的關(guān)閉而刪除。
```
NSString *tempPath = NSTemporaryDirectory( );
```
###NSUerDefault
小規(guī)模數(shù)據(jù),弱業(yè)務(wù)相關(guān)數(shù)據(jù),都可以放到`NSUserDefault`里面。
- 偏好設(shè)置是專門用來保存應(yīng)用程序的配置信息的,一般不要在偏好設(shè)置中保存其他數(shù)據(jù)。
- 如果沒有調(diào)用`synchronize`方法,系統(tǒng)會(huì)根據(jù)I/O情況不定時(shí)刻地保存到文件中。所以如果需要立即寫入文件的就必須調(diào)用`synchronize`方法。
- 偏好設(shè)置會(huì)將所有數(shù)據(jù)保存到同一個(gè)文件中。即`preference`目錄下的一個(gè)以此應(yīng)用包名來命名的plist文件。
```
//1.獲得NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中寫入內(nèi)容
[userDefaults setObject:@"Barry" forKey:@"name"];
[userDefaults setBool:YES forKey:@"male"];
[userDefaults setInteger:20 forKey:@"age"];
//2.1立即同步
[userDefaults synchronize];
//3.讀取文件
NSString *name = [userDefaults objectForKey:@"name"];
BOOL male = [userDefaults boolForKey:@"male"];
NSInteger age = [userDefaults integerForKey:@"age"];
NSLog(@"%@, %d, %ld", name, male, age);
```
###Plist
`plist文件存儲(chǔ)`:是一種明文的輕量級(jí)存儲(chǔ)方式,最常用的格式是XML格式,系統(tǒng)會(huì)提供一個(gè)info.plist文件,這種方式的安全性幾乎為**0**,所以`plist`主要是用于存儲(chǔ)少量并且不重要的數(shù)據(jù)。 `plist`只能讀取數(shù)組(NSArray)或者字典(NSDictonary)。
```
/**
*? Plist文件
*/
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [path stringByAppendingPathComponent:@"Person.plist"];
NSDictionary *dict = @{
@"name" : @"Barry",
@"age" : @20,
@"height" : @1.80f
};
// 將數(shù)據(jù)寫入Plist
[dict writeToFile:fileName atomically:YES];
NSLog(@"%@", fileName);
// 讀取plist中的數(shù)據(jù)
NSDictionary *dicts = [NSDictionary dictionaryWithContentsOfFile:fileName];
NSLog(@"dict = %@",dicts);
NSString *str = [NSString stringWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:nil];
NSLog(@"str = %@", str);
```
###Keychain(鑰匙串)
`Keychain` 是蘋果提供的帶有可逆加密的存儲(chǔ)機(jī)制,普遍用在各種存密碼的需求上。另外,由于App卸載只要系統(tǒng)不重裝,`Keychain`中的數(shù)據(jù)依舊能夠得到保留,以及可被`iCloud`同步的特性,大家都會(huì)在這里存儲(chǔ)用戶唯一標(biāo)識(shí)串。所以有需要加密、需要存`iCloud`的敏感小數(shù)據(jù),一般都會(huì)放在`Keychain`。
###數(shù)據(jù)歸檔/序列化(NSKeyedArchiver)
`NSKeyedArchiver`: 是一種輕量級(jí)存儲(chǔ)的持久化方案,數(shù)據(jù)化歸檔時(shí)經(jīng)過加密處理的,所以安全性遠(yuǎn)高于`plist`。數(shù)據(jù)歸檔可以存儲(chǔ)一些復(fù)雜的對(duì)象,數(shù)據(jù)保存前會(huì)經(jīng)過二進(jìn)制處理。
- 遵循協(xié)議、設(shè)置屬性
```
/*遵循 NSCoding 協(xié)議*/
@interface Student : NSObject
/*設(shè)置屬性*/
@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) int age;
@property(nonatomic, copy) NSString *gender;
@end
```
```
/*解檔(反序列化)*/
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self == [super init]) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntForKey:@"age"];
self.gender = [aDecoder decodeObjectForKey:@"gender"];
}
return self;
}
/**歸檔(序列化)*/
- (void)encodeWithCoder:(NSCoder *)aCoder {
// 歸檔姓名(字符串對(duì)象)
[aCoder encodeObject:self.name forKey:@"name"];
// 歸檔年齡(注意:這是基本數(shù)據(jù)類型, 如果是其他的類型,直接調(diào)用對(duì)應(yīng)類型的encode即可)
[aCoder encodeInteger:self.age forKey:@"age"];
[aCoder encodeObject:self.gender forKey:@"gender"];
}
@end
```
PS:
1. 必須遵循并實(shí)現(xiàn)NSCoding協(xié)議
2. 保存文件的擴(kuò)展名可以任意指定
3. 繼承時(shí)必須先調(diào)用父類的歸檔解檔方法
###數(shù)據(jù)庫(SQLite)
數(shù)據(jù)庫(SQLite3): 是一個(gè)輕量級(jí),跨平臺(tái)的小型數(shù)據(jù)庫,可移植性比較高,有著和`MySql`幾乎相同的數(shù)據(jù)庫語句,以及無需服務(wù)器即可使用的優(yōu)點(diǎn)。
數(shù)據(jù)庫的優(yōu)點(diǎn):
-? 該方案可以存儲(chǔ)大量的數(shù)據(jù),存儲(chǔ)和檢索的速度非常快。
- 能對(duì)數(shù)據(jù)進(jìn)行大量的聚合,這樣比起使用對(duì)象來講操作要快。
數(shù)據(jù)庫的缺點(diǎn):
- 它沒有提供數(shù)據(jù)庫的創(chuàng)建方式。
- 它的底層是基于C語言框架設(shè)計(jì)的,沒有面向?qū)ο蟮腁PI,用起來非常麻煩。
- 發(fā)雜的數(shù)據(jù)模型的數(shù)據(jù)建表,非常麻煩。
在實(shí)際開發(fā)中我們都是使用的是`FMDB`第三方開源的數(shù)據(jù)庫,該數(shù)據(jù)庫是基于`splite`封裝的面向?qū)ο蟮目蚣堋?/p>
###CoreData
`coreData`: 是蘋果官方在iOS5之后推出的綜合性數(shù)據(jù)庫,其使用了對(duì)象關(guān)系映射技術(shù),將對(duì)象轉(zhuǎn)換成數(shù)據(jù),將數(shù)據(jù)存儲(chǔ)在本地的數(shù)據(jù)庫中。
` coreData`為了提高效率,需要將數(shù)據(jù)存儲(chǔ)在不同的數(shù)據(jù)庫中,比如:在使用的時(shí)候,最好是將本地的數(shù)據(jù)保存到內(nèi)存中,這樣的目的是訪問
速度比較快。
PS: CoreData并不是數(shù)據(jù)庫!
使用了對(duì)象關(guān)系映射技術(shù),將對(duì)象轉(zhuǎn)換成數(shù)據(jù),將數(shù)據(jù)存儲(chǔ)在本地的數(shù)據(jù)庫中。底層還是使用 SQLit3。
##下面一篇BLOG,將著重介紹第三方封裝數(shù)據(jù)庫`FMDB`的使用!