你是用什麼方法來持久保存數據的?這是在幾乎每一次關於iOS技術的交流或討論都會被提到的問題,並且你們對這個問題的熱情持續高漲。本文主要從概念上把「數據存儲」這個問題進行剖析,而且結合各自特色和適用場景給你們提供一個選擇的思路,並不詳細介紹某一種方式的技術細節。數據庫
談到數據儲存,首先要明確區分兩個概念,數據結構和儲存方式。所謂數據結構就是數據存在的形式。除了基本的NSDictionary、NSArray和NSSet這些對象,還有更復雜的如:關係模型、對象圖和屬性列表多種結構。而存儲方式則簡單的分爲兩種:內存與閃存。內存存儲是臨時的,運行時有效的,但效率高,而閃存則是一種持久化存儲,但產生I/O消耗,效率相對低。把內存數據轉移到閃存中進行持久化的操做稱成爲歸檔。segmentfault
兩者結合起來纔是完整的數據存儲方案,咱們最常談起的那些:SQLite、CoreData、NSUserDefaults等都是數據存儲方案。固然在這些框架提供的方案以外,咱們本身也能夠按照個性化需求訂製方案。這些存儲方案側重不一樣,支持的形式和方式也各不相同,在不一樣的使用場景下表現也是各有優劣。但萬變不離其宗,不管什麼方案均可以用下圖來解釋。數組
圖1,存儲方案示意圖緩存
如下將對四種存儲方式進行詳細的介紹:數據結構
NSUserDefaults,用於存儲配置信息
SQLite,用於存儲查詢需求較多的數據
CoreData,用於規劃應用中的對象
使用基本對象類型定製的個性化緩存方案
用NSUserDefaults存儲配置信息架構
NSUserDefaults被設計用來存儲設備和應用的配置信息,它經過一個工廠方法返回默認的、也是最經常使用到的實例對象。這個對象中儲存了系統中用戶的配置信息,開發者能夠經過這個實例對象對這些已有的信息進行修改,也能夠按照本身的需求建立新的配置項。框架
圖2,筆者手機中[NSUserDefaults standardUserDefaults]內容性能
NSUserDefaults把配置信息以字典的形式組織起來,支持字典的項包括:字符串或者是數組,除此以外還支持數字等基本格式。一句話歸納就是:基礎類型的小數據的字典。操做方法幾乎與NSDictionary的操做方法無異,另外還能夠經過指定返回類型的方法獲取到指定類型的返回值。spa
圖3,NSUserDefaults提供的指定返回類型的方法列表設計
NSUserDefaults的全部數據都放在內存裏,所以操做速度很快,並還提供一個歸檔方法:+ (void)synchronize。開發者自定義的配置項(如圖2中的最後一項 key:alkdjfkladsjfmm)會以plist格式的文件歸檔在相應應用目錄的/Library/Preferences/[App_Bundle_Identifier].plist文件。再次初始化得到實例對象後,框架會把用戶自定義的這個配置和系統配置合併獲得完整數據。
用SQLite存儲查詢需求較多的數據
iOS的SDK裏預置了SQLite的庫,開發者能夠自建SQLite數據庫。SQLite每次寫入數據都會產生IO消耗,把數據歸檔到相應的文件。
SQLite擅長處理的數據類型其實與NSUserDefaults差很少,也是基礎類型的小數據,只是從組織形式上不一樣。開發者能夠以關係型數據庫的方式組織數據,使用SQL DML來管理數據。 通常來講應用中的格式化的文本類數據能夠存放在數據庫中,尤爲是相似聊天記錄、Timeline等這些具備條件查詢和排序需求的數據。
每個數據庫的句柄都會在內存中都會被分配一段緩存,用於提升查詢效率。另外一個方面,因爲查詢緩存,當產生大量句柄或數據量較大時,會出現緩存過大,形成內存浪費。
SQLite的使用起來要比NSUserDefaults複雜的多,所以建議開發者使用SQLite要搭配一個操做控件使用,能夠簡化操做。筆者開發的SQLight是一款對SQLite操做的封裝,把相對複雜的SQLite命令封裝成對象和方法,能夠供你們參考。你們能夠在Github上獲取這個工程的代碼進一步瞭解。
用CoreData規劃應用中對象
官方給出的定義是,一個支持持久化的,對象圖和生命週期的自動化管理方案。嚴格意義上說CoreData是一個管理方案,他的持久化能夠經過SQLite、XML或二進制文件儲存。如官方定義所說,CoreData的做用遠遠不止儲存數據這麼簡單,它能夠把整個應用中的對象建模並進行自動化的管理。
圖4,官方文檔中解釋CoreData給出的對象圖示例
正如上圖所示,MyDocument是一個對象實例,有兩個Collection:Employee和Department,存放各自的對象列表。MyDocument、Employee和Department三個對象以及他們之間的關係都經過CoreData建模,並能夠經過save方法進行持久化。
從歸檔文件還原模型時CoreData並非一次性把整個模型中的全部數據都載入內存,而是根據運行時狀態,把被調用到的對象實例載入內存。框架會自動控制這個過程,從而達到控制內存消耗,避免浪費。
不管從設計原理仍是使用方法上看,CoreData都比較複雜。所以,若是僅僅是考慮緩存數據這個需求,CoreData絕對不是一個優選方案。CoreData的使用場景在於:整個應用使用CoreData規劃,把應用內的數據經過CoreData建模,徹底基於CoreData架構應用。
蘋果官方給出的一個示例代碼,結構相對簡單,能夠幫助你們入門CoreData。
使用基本對象類型定製的個性化緩存方案
以前提到的NSUserDefaults和SQLite適合存儲基礎類型的小數據,而CoreData則不適合存儲單一的數據,那麼對於相似圖片這種較大的數據要用什麼方式儲存呢?我給出的建議就是:本身實現一套存儲方案。說到訂製存儲方案你們很是容易質疑,這是否是又在從新發明輪子。我能夠很是明確的告訴你們,這毫不是在從新發明輪子。首先要明確,這個所謂的定製方案適用於互聯網應用中對遠程數據的緩存,幾個限制條件缺一不可。
從需求出發分析緩存數據有哪些要求:按Key查找,快速讀取,寫入不影響正常操做,不浪費內存,支持歸檔。這些都是基本需求,那麼再進一步或許還須要固定緩存項數量,支持隊列緩存,緩存過時等。從這些需求入手設計一個緩存方案並不十分複雜,Kache是筆者根據開發應用的需求開發的一套緩存組件,經過分析Kache但願能夠給你們一個思路。
圖5,Kache架構圖
如上圖所示,Kache扮演的是一個典型緩存角色。應用加載遠程數據生成應用數據對象的同時,經過Kache把數據緩存起來,再次請求則直接經過Kache獲取數據。
緩存對象能夠是NSDictionary、NSArray、NSSet或NSData這些可直接歸檔的類型,每一個緩存對象對應一個Key。緩存對象包括數據和過時時間,內存中存放在一個單例字典中,閃存中每一個對象存爲一個文件。Key空間按照各類順序存放緩存對象的Key集合,Pool爲固定大小的數組,當數量達到上限,最先過時的一個Key將被刪除,對應的緩存對象也被清除。Queue也是固定大小的數組,以先進先出的規則管理Key的增刪。 每一次有新的緩存對象存入,自動檢測Key空間中過時的集合並清除。
此外,控件提供save和load方法支持持久化和從新載入。
Kache最初設計爲存放圖片緩存,以後也曾用於緩存文本數據,因爲使用了過時和歸檔相結合的邏輯,能夠保證大部分命中的緩存對象都在內存中,從而獲取了較高的效率。讀者能夠從Github上獲取Kache源碼瞭解更多。
以上介紹了幾種iOS開發中常常會遇到的儲存數據方法,從其存儲原理、使用方式和適用場景幾方面進行進了簡單的對比。事實上每一款應用都很難採用一種單一的方案完成整個應用的數據儲存任務,須要根據不一樣的數據類型,選擇最合適的方案,以便整個應用得到良好的運行時性能。