1 持久化目的
- 快速展示,提升體驗(yàn)
- 已經(jīng)加載過(guò)的數(shù)據(jù),用戶下次查看時(shí),不需要再次從網(wǎng)絡(luò)(磁盤)加載,直接展示給用戶
- 節(jié)省用戶流量(節(jié)省服務(wù)器資源)
- 對(duì)于較大的資源數(shù)據(jù)進(jìn)行緩存,下次展示無(wú)需下載消耗流量
- 同時(shí)降低了服務(wù)器的訪問(wèn)次數(shù),節(jié)約服務(wù)器資源。(圖片)
- 離線使用。
- 用戶瀏覽過(guò)的數(shù)據(jù)無(wú)需聯(lián)網(wǎng),可以再次查看。
- 部分功能使用解除對(duì)網(wǎng)絡(luò)的依賴。(百度離線地圖、圖書閱讀器)
- 無(wú)網(wǎng)絡(luò)時(shí),允許用戶進(jìn)行操作,等到下次聯(lián)網(wǎng)時(shí)同步到服務(wù)端。
- 記錄用戶操作
- 草稿:對(duì)于用戶需要花費(fèi)較大成本進(jìn)行的操作,對(duì)用戶的每個(gè)步驟進(jìn)行緩存,用戶中斷操作后,下次用戶操作時(shí)直接繼續(xù)上次的操作。
- 已讀內(nèi)容標(biāo)記緩存,幫助用戶識(shí)別哪些已讀。
- 搜索記錄緩存
2 數(shù)據(jù)持久化方式分類
2.1 內(nèi)存緩存
- 定義
對(duì)于使用頻率比較高的數(shù)據(jù),從網(wǎng)絡(luò)或者磁盤加載數(shù)據(jù)到內(nèi)存以后,使用后并不馬上銷毀,下次使用時(shí)直接從內(nèi)存加載。 - 案例
iOS系統(tǒng)圖片加載——[UIImage imageNamed:@"imageName"]
網(wǎng)絡(luò)圖片加載三方庫(kù):SDWebImage
- 實(shí)現(xiàn)
實(shí)現(xiàn)內(nèi)存緩存的技術(shù)手段包括蘋果官方提供的NSURLCache
,NSCache
,還有性能和API上比較有優(yōu)勢(shì)的開(kāi)源緩存庫(kù)YYCache
、PINCache
等。
2.2 磁盤緩存
- 定義
將從網(wǎng)絡(luò)加載的、用戶操作產(chǎn)生的數(shù)據(jù)寫入到磁盤,用戶下次查看、繼續(xù)操作時(shí),直接從磁盤加載使用。 - 案例
用戶輸入內(nèi)容草稿緩存(如:評(píng)論、文本編輯)
網(wǎng)絡(luò)圖片加載三方庫(kù):SDWebImage
搜索歷史緩存 - 實(shí)現(xiàn)
-
NSUserDefault
適合小規(guī)模數(shù)據(jù),弱業(yè)務(wù)相關(guān)數(shù)據(jù)的緩存。 -
keychain
Keychain
是蘋果提供的帶有可逆加密的存儲(chǔ)機(jī)制,普遍用在各種存用戶名、密碼的需求上。另外,Keychain
是系統(tǒng)級(jí)存儲(chǔ),還可以被iCloud
同步,即使App
被刪除,Keychain
數(shù)據(jù)依然保留,用戶下次安裝App
,可以直接讀取,通常會(huì)用來(lái)存儲(chǔ)用戶唯一標(biāo)識(shí)串。所以需要加密、同步iCloud
的敏感小數(shù)據(jù),一般使用Keychain
存取。 - 文件存儲(chǔ)
-
Plist
:一般結(jié)構(gòu)化的數(shù)據(jù)可以Plist
的方式去持久化 -
archive
:Archive
方式可以存取遵循協(xié)議的數(shù)據(jù),比較方便的是存取使用的都是對(duì)象,不過(guò)中間的序列化和反序列化需要花費(fèi)一定的性能,可以在想要使用對(duì)象直接進(jìn)行磁盤存取時(shí)使用。 -
Stream
:指文件存儲(chǔ),一般用來(lái)存圖片、視頻文件等數(shù)據(jù)
-
- 數(shù)據(jù)庫(kù)存儲(chǔ)
數(shù)據(jù)庫(kù)適合存取一些關(guān)系型的數(shù)據(jù);可以在有大量的條件查詢排序類需求時(shí)使用。-
Core Data
:蘋果官方封裝的ORM(Object Relational Mapping)
-
FMDB
:github
最受歡迎的iOS sqlite
封裝開(kāi)源庫(kù)之一 -
WCDB
:微信團(tuán)隊(duì)在自己使用的sqlite封裝基礎(chǔ)上的開(kāi)源實(shí)現(xiàn),具有ORM(Object Relational Mapping)
的特性,支持iOS
、Android
。 -
Realm
:由Y Combinator
孵化的創(chuàng)業(yè)團(tuán)隊(duì)開(kāi)源出來(lái)的一款跨平臺(tái)(iOS
、Android
)移動(dòng)數(shù)據(jù)庫(kù)。
-
-
2.3 應(yīng)該用哪種緩存方案
根據(jù)需求選擇:
- 簡(jiǎn)單數(shù)據(jù)存儲(chǔ)直接寫文件、key-value存取即可。
- 需要按照一些條件查找、排序等需求的,可以使用sqlite等關(guān)系型存儲(chǔ)方式。
- 不希望App刪除后清除的小容量數(shù)據(jù)(用戶名、密碼、token)存keychain。
3 沙盒機(jī)制介紹
iOS
中的沙盒機(jī)制是一種安全體系。為了保證系統(tǒng)安全,iOS
每個(gè)應(yīng)用程序在安裝時(shí),會(huì)創(chuàng)建屬于自己的沙盒文件(存儲(chǔ)空間)。應(yīng)用程序只能訪問(wèn)自身的沙盒文件,不能訪問(wèn)其他應(yīng)用程序的沙盒文件,當(dāng)應(yīng)用程序需要向外部請(qǐng)求或接收數(shù)據(jù)時(shí),都需要經(jīng)過(guò)權(quán)限認(rèn)證,否則,無(wú)法獲取到數(shù)據(jù)。所有的非代碼文件都要保存在此,例如屬性文件plist
、文本文件、圖像、圖標(biāo)、媒體資源等。
3.1 沙盒機(jī)制與持久化的關(guān)系
iOS 中的持久化是指將數(shù)據(jù)在應(yīng)用程序退出后仍然保留在存儲(chǔ)設(shè)備上的能力。而沙盒機(jī)制是 iOS 系統(tǒng)為每個(gè)應(yīng)用程序提供的安全性措施,通過(guò)將每個(gè)應(yīng)用程序限制在自己的沙盒中,確保應(yīng)用程序只能訪問(wèn)自己的數(shù)據(jù),并不能訪問(wèn)其他應(yīng)用程序的數(shù)據(jù)。
持久化和沙盒機(jī)制在 iOS
應(yīng)用程序中是密切相關(guān)的,因?yàn)閼?yīng)用程序需要在沙盒中對(duì)數(shù)據(jù)進(jìn)行持久化。iOS
提供了幾種方式來(lái)實(shí)現(xiàn)數(shù)據(jù)持久化,而所有這些方式都是在應(yīng)用程序的沙盒內(nèi)進(jìn)行的。以下是 iOS
持久化與沙盒機(jī)制的關(guān)系:
- 文件儲(chǔ)存持久化: 在
iOS
應(yīng)用程序中,可以使用文件系統(tǒng)來(lái)持久化數(shù)據(jù),例如將數(shù)據(jù)保存到文件中。iOS
應(yīng)用程序只能在自己的沙盒目錄中創(chuàng)建、讀取和寫入文件,不能訪問(wèn)其他應(yīng)用程序的文件。常見(jiàn)的文件系統(tǒng)持久化方式包括使用NSFileManager
類進(jìn)行文件操作,或者使用NSData
或NSString
類的方法將數(shù)據(jù)寫入文件。 -
UserDefaults
持久化:NSUserDefaults
是一種簡(jiǎn)單的持久化方式,可以用于保存用戶的偏好設(shè)置、配置信息等。雖然數(shù)據(jù)是持久化的,但是它們?nèi)匀淮鎯?chǔ)在應(yīng)用程序的沙盒中,并且只能被當(dāng)前應(yīng)用程序訪問(wèn)。 - 數(shù)據(jù)庫(kù)持久化:它將數(shù)據(jù)保存到
SQLite
數(shù)據(jù)庫(kù)中或其他數(shù)據(jù)存儲(chǔ)。這些也是在應(yīng)用程序的沙盒內(nèi)進(jìn)行數(shù)據(jù)持久化的方式。 -
Keychain
持久化:Keychain
是iOS
提供的一種安全存儲(chǔ)敏感數(shù)據(jù)的方式,例如密碼、密鑰等。Keychain
中的數(shù)據(jù)也是在應(yīng)用程序的沙盒內(nèi)進(jìn)行持久化,并且具有更高的安全性保護(hù),確保只有應(yīng)用程序本身才能訪問(wèn)這些數(shù)據(jù)。
總的來(lái)說(shuō),iOS
中的持久化操作都是在應(yīng)用程序的沙盒內(nèi)進(jìn)行的,受到沙盒機(jī)制的限制,確保應(yīng)用程序只能訪問(wèn)自己的數(shù)據(jù),保證了應(yīng)用程序之間的數(shù)據(jù)隔離和安全性。
3.2 沙盒的目錄結(jié)構(gòu)
3.2.1 獲取應(yīng)用程序的沙盒路徑:
// 獲取沙盒根目錄路徑
NSString *path = NSHomeDirectory();
注意: 每次編譯代碼會(huì)生成新的沙盒路徑,注意是編譯不是啟動(dòng),所以模擬機(jī)或者真機(jī)運(yùn)行,每次運(yùn)行所得到的沙盒路徑都是不一樣的,線上版本app真機(jī)不會(huì)生成新的沙盒路徑。
上面的代碼得到的就是當(dāng)前應(yīng)用程序目錄的路徑,該目錄下就是應(yīng)用程序的沙盒,在該目錄下有4個(gè)文件夾:Documents
、Library
、SystemData
、tmp
,當(dāng)前應(yīng)用程序只能訪問(wèn)該目錄下的文件。
3.2.2 訪問(wèn)沙盒目錄常用C函數(shù)介紹
//文件路徑搜索
FOUNDATION_EXPORT NSArray<NSString *> *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
該方法返回值為一個(gè)數(shù)組,在iphone中由于只有一個(gè)唯一路徑,所以直接取數(shù)組第一個(gè)元素即可。
該函數(shù)的作用是返回指定搜索目錄下的路徑數(shù)組。它接受三個(gè)參數(shù):
-
directory
:NSSearchPathDirectory
類型的枚舉值,用于指定搜索的目錄。常見(jiàn)的枚舉值包括:
//常用的NSSearchPathDirectory枚舉值
typedef NS_ENUM(NSUInteger, NSSearchPathDirectory) {
NSApplicationDirectory = 1, // supported applications (Applications)
NSDemoApplicationDirectory, // unsupported applications, demonstration versions (Demos)
NSAdminApplicationDirectory, // system and network administration applications (Administration)
NSLibraryDirectory, // various documentation, support, and configuration files, resources (Library)
NSUserDirectory, // user home directories (Users)
NSDocumentationDirectory, // Library 下的(Documentation)模擬器上沒(méi)有創(chuàng)建
NSDocumentDirectory, // documents (Documents)
};
-
domainMask
:NSSearchPathDomainMask
類型的位掩碼,用于指定搜索路徑的范圍。常見(jiàn)的枚舉值包括:
typedef NS_OPTIONS(NSUInteger, NSSearchPathDomainMask) {
NSUserDomainMask = 1, // 用戶目錄 - 基本上就用這個(gè)。
NSLocalDomainMask = 2, // 本地
NSNetworkDomainMask = 4, // 網(wǎng)絡(luò)
NSSystemDomainMask = 8, // 系統(tǒng)
NSAllDomainsMask = 0x0ffff // 所有
};
-
expandTilde
:BOOL
類型的參數(shù),指定是否展開(kāi)波浪號(hào)~
。如果設(shè)置為 YES,則路徑中的波浪號(hào)~
將被展開(kāi)為用戶的主目錄路徑。
該值為NO:Caches目錄路徑為~/Library/Caches
該值為YES:Caches目錄路徑為/var/mobile/Containers/Data/Application/E7B438D4-0AB3-49D0-9C2C-B84AF67C752B/Library/Caches
3.2.3 沙盒目錄的獲取示例
//獲取沙盒根路徑
NSString *path = NSHomeDirectory();
NSLog(@"沙盒根路徑:%@", path);
//Document路徑
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSLog(@"Document目錄路徑:%@", docDir);
// 獲取Library的目錄路徑
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSLog(@"Libarary目錄路徑:%@", libDir);
// 獲取Caches目錄路徑
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
NSLog(@"Cacheas目錄路徑:%@", cachesDir);
// library Preference
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(@"偏好設(shè)置目錄路徑:%@", defaults);
// 獲取tmp目錄路徑
NSString *tmpDir = NSTemporaryDirectory();
NSLog(@"tmp目錄路徑:%@", tmpDir);
輸出結(jié)果:
3.2.4 沙盒目錄介紹
-
Documents
:保存持久化數(shù)據(jù),會(huì)備份。一般用來(lái)存儲(chǔ)需要持久化的數(shù)據(jù)。
一般我們?cè)陧?xiàng)目中,我們會(huì)把一些用戶的登錄信息以及搜索歷史記錄等一些關(guān)鍵數(shù)據(jù)存儲(chǔ)到這里。
// 獲取Documents目錄路徑
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
此文件夾是默認(rèn)備份的,備份到iCloud。
注:iCloud的備份,會(huì)通過(guò)Wi-Fi每天自動(dòng)備份用戶iOS設(shè)備。
我們可以在獲取到的路徑結(jié)尾加一個(gè)字符串來(lái)創(chuàng)建一個(gè)文件名:
NSString *filename = [docDir stringByAppendingPathComponent:@"data.txt"];
這樣我們的這個(gè)filename
就是一個(gè)完整的.txt
類型文件的目錄了。
-
Library
:默認(rèn)存放設(shè)置和其他狀態(tài)信息,除了caches
子目錄之外其他目錄都會(huì)被iclude
同步。-
Application Support
:此目錄包含應(yīng)用程序用來(lái)運(yùn)行但應(yīng)對(duì)用戶隱藏的文件,如游戲的新關(guān)卡等文件。 -
Caches
:保存應(yīng)用運(yùn)行時(shí)生成的需要持久化的數(shù)據(jù),一般存儲(chǔ)體積大、不需要備份的非重要數(shù)據(jù),如網(wǎng)絡(luò)請(qǐng)求的音視頻與圖片等的緩存。在iOS 5.0
及以后版本中,Caches
當(dāng)系統(tǒng)磁盤空間非常低時(shí),系統(tǒng)可能會(huì)在極少數(shù)情況下該刪除目錄(APP 正在運(yùn)行時(shí)不會(huì)發(fā)生),所以盡量保證該路徑的文件在 APP 在重新運(yùn)行時(shí)可以得到重新創(chuàng)建。 -
Cooikes
:系統(tǒng)會(huì)自動(dòng)將App
中網(wǎng)絡(luò)請(qǐng)求的cookie
保存為文件。 -
Preferences
:保存應(yīng)用的所有偏好設(shè)置。UserDefaults
生成的plist
文件就會(huì)保存該目錄下。 -
SplashBoard
:存儲(chǔ)啟動(dòng)屏緩存,緩存文件格式為ktx
,本質(zhì)上就是圖片,如果啟動(dòng)屏不生效的問(wèn)題可以考慮從刪除該路徑下相關(guān)緩存文件這個(gè)角度解決。
-
-
SystemData
:存放系統(tǒng)數(shù)據(jù),無(wú)對(duì)外暴露的接口。 -
tmp
:臨時(shí)文件夾(系統(tǒng)會(huì)不定期刪除里面的文件)。
4 持久化數(shù)據(jù)存儲(chǔ)方式
4.1 plist文件(序列化)
plist文件是通過(guò)XML文件的方式保存在目錄中 以下類型可以被序列化:
NSString;//字符串
NSMutableString;//可變字符串
NSArray;//數(shù)組
NSMutableArray;//可變數(shù)組
NSDictionary;//字典
NSMutableDictionary;//可變字典
NSData;//二進(jìn)制數(shù)據(jù)
NSMutableData;//可變二進(jìn)制數(shù)據(jù)
NSNumber;//基本數(shù)據(jù)
NSDate;//日期
這里我們就用NSDictionary當(dāng)例子,其他的類型和這個(gè)方法類似:
- (void) writeToPlist {
// 定義plistName
NSString *plistName = @"test.plist";
// 存取路徑
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 打印路徑
NSLog(@"%@", path);
// 連接路徑
NSString *filePath = [path stringByAppendingPathComponent:plistName];
// 定義字典文件
NSDictionary *dict = @{@"SXTTTTT":@"10"};
//序列化
[dict writeToFile:filePath atomically:YES];
}
atomically是否先寫入輔助文件,增加安全性的寫入文件方法,一般都是YES。
前往打印出的路徑查看,發(fā)現(xiàn)確實(shí)多了一個(gè)test.plist
文件。
打開(kāi)這個(gè)文件看看。
可以看到值確實(shí)儲(chǔ)存到了這個(gè)文件內(nèi)部。
使用dictionaryWithContentsOfFile:
類方法可以取出這個(gè)值:
NSDictionary *dict1 = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(@"%@", dict1);
結(jié)果:
4.2 preference:偏好設(shè)置
偏好設(shè)置(Preferences
)是 iOS 開(kāi)發(fā)中一種簡(jiǎn)單的數(shù)據(jù)持久化方式,用于保存應(yīng)用程序的配置信息、用戶偏好設(shè)置等數(shù)據(jù)。在 iOS 中,可以使用 NSUserDefaults
類來(lái)進(jìn)行偏好設(shè)置的讀取和保存。
NSUserDefaults
是一個(gè)單例類,用于訪問(wèn)應(yīng)用程序的偏好設(shè)置。它可以存儲(chǔ)各種類型的數(shù)據(jù),如布爾值、整數(shù)、浮點(diǎn)數(shù)、字符串、數(shù)組、字典等。這些數(shù)據(jù)將會(huì)持久化到應(yīng)用程序的沙盒中,即使應(yīng)用程序退出,下次啟動(dòng)時(shí)也可以讀取這些數(shù)據(jù)。
NSUserDefaults
:簡(jiǎn)單數(shù)據(jù)快速讀寫,不能存儲(chǔ)自定義類型。UserDefaults
設(shè)置數(shù)據(jù)時(shí),不是立即寫入,而是根據(jù)時(shí)間戳定時(shí)地把緩存中的數(shù)據(jù)寫入本地磁盤。所以調(diào)用了set
方法之后數(shù)據(jù)有可能還沒(méi)有寫入磁盤應(yīng)用程序就終止了。出現(xiàn)以上問(wèn)題,可以通過(guò)調(diào)用synchornize
方法[defaults synchornize];
強(qiáng)制寫入。
偏好設(shè)置存儲(chǔ)的優(yōu)點(diǎn):
- 不需要關(guān)心文件名,系統(tǒng)會(huì)自動(dòng)幫你生成一個(gè)文件名。
- 快速做鍵值對(duì)的存儲(chǔ)。
我們來(lái)嘗試使用使用UserDefaults
注冊(cè)一個(gè)賬號(hào)密碼
- (void)writeToUserDefaults {
// 獲取偏好設(shè)置對(duì)象
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 存儲(chǔ)數(shù)據(jù)
[defaults setObject:@"ouhaijian" forKey:@"name"];
[defaults setObject:@"666666" forKey:@"password"];
// 同步調(diào)用,立刻寫到文件中,不寫這個(gè)方法會(huì)異步,有延遲
[defaults synchronize];
}
- (void)readFromUserDefaults {
// 獲取偏好設(shè)置對(duì)象
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 獲取數(shù)據(jù)
NSString *name = [defaults objectForKey:@"name"];
NSString *password = [defaults objectForKey:@"password"];
NSLog(@"name:%@", name);
NSLog(@"password:%@", password);
}
運(yùn)行結(jié)果:
注意,對(duì)于設(shè)置偏好設(shè)置值后,需要調(diào)用 synchronize 方法來(lái)立即將數(shù)據(jù)保存到磁盤中,以確保數(shù)據(jù)持久化。不過(guò),iOS 7 及以上版本,synchronize 方法會(huì)自動(dòng)在合適的時(shí)機(jī)將數(shù)據(jù)保存,所以不再是必需的操作。
我們到cd到沙盒中的preference
文件目錄,可以看到里面多了一個(gè)新的文件,這個(gè)文件里面儲(chǔ)存的就是我們儲(chǔ)存的賬號(hào)。
打開(kāi)這個(gè)文件看看。
4.3 NSKeyedArchiver歸檔和解檔
NSKeyedArchiver
(歸檔):歸檔一般都是保存自定義對(duì)象的時(shí)候,使用歸檔。因?yàn)?code>plist文件不能夠保存自定義對(duì)象。如果一個(gè)字典中保存有自定義對(duì)象,如果把這個(gè)對(duì)象寫入到文件當(dāng)中,它是不會(huì)生成 plist
文件的。如果對(duì)象是NSString
、NSDictionary
、NSArray
、NSData
、NSNumber
等類型,可以直接用NSKeyedArchiver
進(jìn)行歸檔和恢復(fù)。
但是在我們使用歸檔之前,我們必須得遵守NSSecureCoding
協(xié)議才行,老版本只需要遵循NSCoding
實(shí)現(xiàn)其歸檔和解檔的方法就行,但是iOS13更新之后就不行了,我們就必須的遵守NSSecureCoding
協(xié)議,NSSecureCoding
協(xié)議也遵循了原來(lái)NSCoding
這個(gè)協(xié)議,不過(guò)我們還需要遵循它的一個(gè)supportsSecureCoding
方法,這樣我們才能歸檔成功。
因?yàn)?code>NSSecureCoding協(xié)議也遵循了原來(lái)NSCoding
這個(gè)協(xié)議,所以他也就有了- (void)encodeWithCoder:(NSCoder *)coder
方法和- (id)initWithCoder:(NSCoder *)coder
方法:
-(void)encodeWithCoder:(NSCoder *)coder,每次歸檔對(duì)象時(shí),都會(huì)調(diào)用這個(gè)方法。一般在這個(gè)方法里面指定如何歸檔對(duì)象中的每個(gè)實(shí)例變量??梢允褂胑ncodeObject:forKey:方法歸檔實(shí)例變量。
-(id)initWithCoder:(NSCoder *)coder,每次從文件中會(huì)恢復(fù)(解碼)對(duì)象時(shí),都會(huì)調(diào)用這個(gè)方法。一般在這個(gè)方法里面指定如何解碼文件中的數(shù)據(jù)為對(duì)象的實(shí)例變量,可以使用decodeObject:forKey方法解碼實(shí)例變量。
例,新建一個(gè)SXPerson類:
#import <Foundation/Foundation.h>
@interface SXPerson : NSObject <NSSecureCoding>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) int age;
@end
@implementation SXPerson
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.name forKey:@"name"];
[coder encodeInt:self.age forKey:@"age"];
}
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super init]) {
self.name = [coder decodeObjectForKey:@"name"];
self.age = [coder decodeIntForKey:@"age"];
}
return self;
}
+ (BOOL)supportsSecureCoding {
return YES;
}
@end
進(jìn)行歸檔和解檔:
// 歸檔
- (void)writeUseNSKeyedArchiver {
SXPerson *person = [[SXPerson alloc] init];
person.age = 10;
person.name = @"蔡徐坤";
NSError * __autoreleasing *error = NULL;
// 獲得Document的全路徑
NSString *docu = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 獲得新文件的全路徑
NSString *path = [docu stringByAppendingPathComponent:@"SXPerson.data"];
// 將對(duì)象封裝為Data并歸檔
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:person requiringSecureCoding:YES error:error];
if (error) {
NSLog(@"writeUseNSKeyedArchiver:%@", *error);
} else {
[data writeToFile:path atomically:YES];
}
}
// 解檔
- (void)readUseNSKeyedArchiver {
NSError * __autoreleasing *error = NULL;
// 獲得Document的全路徑
NSString *docu = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 獲得新文件的全路徑
NSString *path = [docu stringByAppendingPathComponent:@"SXPerson.data"];
// 從path路徑中獲取data數(shù)據(jù)
NSData *unData = [NSData dataWithContentsOfFile:path];
// 創(chuàng)建解檔操作的客戶端允許的類集合
// 這個(gè)集合應(yīng)包括自定義對(duì)象的屬性的類
NSSet *allowedClassesSet = [NSSet setWithObjects:[NSString class], [SXPerson class], nil];
SXPerson *person = (SXPerson *)[NSKeyedUnarchiver unarchivedObjectOfClasses:allowedClassesSet fromData:unData error:error];
// 打印結(jié)果
if (error) {
NSLog(@"readUseNSKeyedArchiver:%@", *error);
} else {
NSLog(@"%@-%d", person.name, person.age);
}
}
創(chuàng)建解檔操作的客戶端允許的類集合,這個(gè)集合應(yīng)包括自定義對(duì)象的屬性的類,否則會(huì)給出警告。這個(gè)警告并不會(huì)導(dǎo)致解檔操作出錯(cuò),但是在將來(lái)的版本中可能會(huì)被禁止。為了避免將來(lái)的兼容性問(wèn)題,建議您在解檔時(shí)將需要解檔的所有類都添加到客戶端允許的類集合中。
結(jié)果:
我們還可以看到Document
目錄下多了一個(gè)SXStudent.data
文件:
可以使用vim
打開(kāi)這個(gè)文件看一下,大概能看懂是儲(chǔ)存了NSKeyedArchiver歸檔的一個(gè)對(duì)象:
最后另外說(shuō)一點(diǎn),歸檔操作有時(shí)會(huì)被用于實(shí)現(xiàn)對(duì)象(尤其是自定義對(duì)象)的深拷貝操作。通過(guò)歸檔,我們可以將原對(duì)象序列化為二進(jìn)制數(shù)據(jù),并在反歸檔時(shí)重新創(chuàng)建出一個(gè)全新的對(duì)象,包括其內(nèi)部引用對(duì)象。這樣,新對(duì)象與原對(duì)象之間是完全獨(dú)立的,修改其中一個(gè)對(duì)象不會(huì)影響到另一個(gè)對(duì)象。
4.4 數(shù)據(jù)庫(kù)存儲(chǔ)(這部分太多了,以后有機(jī)會(huì)另學(xué))
-
SQLite
:
是目前主流的嵌入式關(guān)系型數(shù)據(jù)庫(kù),其最主要的特點(diǎn)就是輕量級(jí)、跨平臺(tái),當(dāng)前很多嵌入式操作系統(tǒng)都將其作為數(shù)據(jù)庫(kù)首選。 -
CoreData
:CoreData
是iOS5之后才出現(xiàn)的一個(gè)框架,本質(zhì)上是對(duì)SQLite
的一個(gè)封裝,它提供了對(duì)象-關(guān)系映射(ORM
)的功能,即能夠?qū)C對(duì)象轉(zhuǎn)化成數(shù)據(jù),保存在SQLite
數(shù)據(jù)庫(kù)文件中,也能夠?qū)⒈4嬖跀?shù)據(jù)庫(kù)中的數(shù)據(jù)還原成OC
對(duì)象,在這個(gè)過(guò)程中不需要手動(dòng)編寫任何SQL
語(yǔ)句,CoreData
封裝了數(shù)據(jù)庫(kù)的操作過(guò)程,以及數(shù)據(jù)庫(kù)中數(shù)據(jù)和OC
對(duì)象的轉(zhuǎn)換過(guò)程。通過(guò)CoreData
管理應(yīng)用程序的數(shù)據(jù)模型,可以極大程度減少需要編寫的代碼數(shù)量。 -
FMDB
:
是一個(gè)處理數(shù)據(jù)存儲(chǔ)的第三方框架,框架是對(duì)sqlite
的封裝,整個(gè)框架非常輕量級(jí)但又不失靈活性,而且更加面向?qū)ο蟆?/li> -
SQLite
和CoreData
的區(qū)別:CoreData
可以在一個(gè)對(duì)象更新時(shí),其關(guān)聯(lián)的對(duì)象也會(huì)隨著更新,相當(dāng)于你更新一張表時(shí),其關(guān)聯(lián)的其他表的也會(huì)隨著更新。CoreData
供更簡(jiǎn)單的性能管理機(jī)制,可以限制查詢記錄的總數(shù),這個(gè)類會(huì)自動(dòng)更新其緩存。
多表查詢方面,CoreData
沒(méi)有SQL
直觀,沒(méi)有類似外連接,左連接等操作。
5 補(bǔ)充
5.1 什么是序列化和反序列化,用來(lái)做什么?
- 序列化:把對(duì)象轉(zhuǎn)化為字節(jié)序列的過(guò)程
- 反序列化:把字節(jié)序列恢復(fù)成對(duì)象
- 作用:把對(duì)象寫到文件或者數(shù)據(jù)庫(kù)中,并且讀取出來(lái)
5.2 程序中的plist文件
plist
全名Property List
,屬性列表文件,它是一種用來(lái)存儲(chǔ)串行化后的對(duì)象的文件,屬性列表文件的擴(kuò)展名為plist
,因此通常被稱為plist
文件。文件是xml
格式的。
這里補(bǔ)充一下程序中的plist文件,及其對(duì)應(yīng)的寫入,讀取操作。
程序中的plist文件必須先創(chuàng)建,后使用:
源代碼:
// 寫入程序plist
- (void)setDataToPlist {
// 第一參數(shù):文件名
// 第二參數(shù):文件后綴
NSString *plist = [[NSBundle mainBundle] pathForResource:@"show" ofType:@"plist"];
NSDictionary *dict = @{@"蔡徐坤":@"name", @10:@"age"};
[dict writeToFile:plist atomically:YES];
}
// 讀取程序plist
- (void)getDataFromPlist {
NSString *plist = [[NSBundle mainBundle] pathForResource:@"show" ofType:@"plist"];
NSLog(@"%@", plist);
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plist];
NSLog(@"%@", dict);
}
運(yùn)行結(jié)果:
雖然我們?cè)陧?xiàng)目工程里創(chuàng)建了這個(gè)文件,但是數(shù)據(jù)似乎并不會(huì)保存在這個(gè)文件里,至少不是這個(gè)工程包里的文件。
我們cd到打印出來(lái)的目錄中:
使用vim看看這個(gè)同名文件:
可以看到數(shù)據(jù)被保存到應(yīng)用程序的資源束(Resource Bundle)中。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-612465.html
當(dāng)我們編譯項(xiàng)目時(shí),Xcode似乎會(huì)將資源文件(包括 .plist 文件)全復(fù)制到應(yīng)用程序的資源束(Resource Bundle)中。這樣可以確保在運(yùn)行時(shí)能夠找到并正確讀取資源文件中的內(nèi)容。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-612465.html
到了這里,關(guān)于【iOS】iOS持久化的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!