iOS: 數據持久化方案

數據持久化方案(若是總結不到位,或者有誤的地方,敬請斧正)sql

 

1、功能:數據庫

    主要是將數據持久化到本地,減小對網絡請求的次數,既節省了用戶的流量,也加強了App的體驗效果。數組

 

2、種類:緩存

     plist存儲:使用XML鍵值對持久化,它適用於少許且數據基本不怎麼改變的狀況。安全

        偏好存儲:使用NSUserDefalut持久化,專門用來保存應用程序的配置信息的,通常不要在偏好設置中保存其餘數據。網絡

        歸檔序列化存儲:使用二進制序列化持久化,只要遵循了NSCoding協議的對象均可以經過它實現序列化。多線程

        沙盒存儲:持久化在Document目錄下,通常持久化一些文件,好比圖片,音頻,視頻等,文件沙盒存儲主要存儲非機密數據。app

        本地數據庫存儲:適合儲存大規模數據,管理方便,不過操做稍微複雜一些。框架

 

3、詳解:函數

一、plist存儲

定義:

  plist文件,即屬性列表文件,全名是Property List,這種文件的擴展名爲.plist,所以,一般被叫作plist文件。

做用:

  它是一種用來存儲串行化後的對象的文件,在iOS開發中一般用來存儲用戶設置,還能夠用於存儲程序中常常用到而不常常改動的數據。

問題:

(1)什麼數據適合存儲?

    能存儲NSString、NSArray、NSDictionary、NSData、NSDate、NSNumber、Boolean不能存儲自定義對象   

(2)存到什麼地方?

    寫入建立的.plist文件中

(3)使用場景?

    plist經常使用於存儲長時間不容易發生變化的數據,例如省市列表、車輛名稱列表之類的數據等,這些數據能夠保存在 plist 文件裏,因此plist適用於存儲小型數據,不推薦用plist作緩衝。        

(4)如何使用?

   存儲 

[dict writeToFile:filePath atomically:YES]; // 字典
[array writeToFile:filePath atomically:YES]; // 數組
[string writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil]; // 字符串

    獲取

NSArray *arr = [NSArray arrayWithContentsOfFile:filePath];  // 數組
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath]; // 字典  
NSString *string = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; // 字符串       

(5)有什麼缺點?

          由於全部的數據都放在root dictionary裏,每次讀取都要把整個root dictionary取出來再取須要的對象.若是plist文件緩存了幾十M的數據.這樣很費內存和時間。

 

二、偏好存儲

定義:

  User Defaults 顧名思義就是一個用戶爲系統以及程序設置的默認值。

  每一個用戶都有本身的一套數據,用戶和用戶之間無法共享的。在蘋果的API中,提供了一個類去存儲用戶的偏好設置。

  這個方法推薦只存儲用戶的偏好設置,不要存儲一些字典、數組之類的。

做用:

  不少iOS應用都支持偏好設置,好比保存用戶名、密碼、字體大小等設置

   iOS提供了一套標準的解決方案來爲應用加入偏好設置功能,就是每個app都有一個plist文件專門用以保存偏好設置數據。

  每一個應用都有個NSUserDefaults實例,經過它來存取偏好設置。

問題:

(1)什麼數據適合存儲?

    能夠存儲OC定義的全部數據類型,包括對象(系統和自定義的)類型、基本數據類型,如NSInteger等。

(2)存到什麼地方?

    NSUserDefault 本地保存的位置是Library/Preferences 這個目錄下的 plist 文件。  

(3)使用場景?           

         在App中,有時候咱們須要將一些信息進行短時間的保存,方便用戶下次更方便使用App,減小多餘的操做,加強用戶體驗。

         好比,保存用戶名、字體大小、是否自動登陸等。

(4)如何使用? 

        存儲

NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; [defaults setObject: forKey:]; 
[defaults setInteger:forKey:]; 
[defaults setDouble: forKey:]; 
[defaults setObject: forKey:];
[defaults synchronize];

    獲取 

NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
[defaults objectForKey:];
[defaults objectForKey:];
[defaults integerForKey:];
[defaults doubleForKey:];

(5)注意事項?

  • 偏好設置是專門用來保存應用程序的配置信息的, 通常狀況不要在偏好設置中保存其餘數據。
  • 若是利用系統的偏好設置來存儲數據, 默認就是存儲在Preferences文件夾下面的,偏好設置會將全部的數據都保存到同一個文件中。
  • 使用偏好設置對數據進行保存以後, 它保存到系統的時間是不肯定的,會在未來某一時間點自動將數據保存到Preferences文件夾下面,若是須要即刻將數據存儲,可使用[defaults synchronize];
  • 全部的信息都寫在一個文件中,對比簡單的plist能夠保存和讀取基本的數據類型。

 

三、歸檔序列化存儲

定義:

         對象歸檔是iOS中數據持久化的一種方式。 歸檔是指另外一種形式的二進制序列化,但它是任何對象均可以實現的更常規的類型。

做用:

         使用對模型對象進行歸檔的技術能夠輕鬆將複雜的對象寫入文件,而後再從中讀取它們。

問題:

(1)什麼數據適合存儲?

    要使用對象歸檔,則歸檔的對象所屬類中實現的每一個屬性都是標量,或者都是遵循NSCoding協議和NSCopying協議的某個類的實例對象。

(2)存到什麼地方?

    對象歸檔後將獲得一個後綴爲.archive的文件,數據就保存在了這個文件中。

(3)使用場景?

    定義某個實例,若是須要持久化該實例從而方便之後使用它的屬性值,同時能夠隨意更改該實例的屬性值,推薦在給實例初始化的同時直接使用歸檔進行存儲。       

(4)如何使用?

          遵循NSCoding協議

  • NSCoding中聲明瞭兩個方法,其中一個用於將對象編碼到歸檔中,另外一個方法對歸檔解碼來建立一個新對象。
  • 歸檔時要實現的方法爲:-(void)encodeWithCoder:(NSCoder *)aCoder;
  • 可使用KVC(Key-Value Coding,鍵值編碼)對對象和原生數據類型進行編碼和解碼。
  • 若要對對象進行歸檔,必須使用正確的編碼方法將全部實例變量編碼成encoder。
  • 在解碼時,實現一個經過NSCoder解碼的對象初始化方法,就能夠恢復以前歸檔的對象。
  • 解碼時要實現的方法爲:-(id)initWithCoder:(NSCoder *)aDecode;

          實現NSCopying協議

  • 除了要遵循NSCoding協議外,還要求要使用歸檔的類實現NSCoping協議。 用來複制對象。
  • 這個協議中有一個copywithZone方法:- (id)copyWithZone:(NSZone *)zone;
  • 其實現與inetWitheCoder很是類似,只需建立一個同一類的新實例,而後將新實例的全部屬性都設置爲與該對象屬性相同的值。

     存儲 

NSMutableData *data = [[NSMutableData alloc] init]; //聲明一個二進制流 data,開闢了一個空間
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data]; //聲明一個歸檔類,把歸檔類的內容放入data中   
[archiver encodeObject:id forKey:Key]; //用Key進行編碼 [archiver finishEncoding]; //結束編碼 [data writeToFile:filePath atomically:YES];//編碼結束後,歸檔類的內容已經放入data中了,此時data仍然駐留在內存中,須要寫入文件中

      獲取

NSData *data = [[NSMutableData alloc] initWithContentsOfFile:filePath];//用歸檔文件中的數據初始化
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];//聲明一個解歸檔對象,把data中的數據複製給解歸檔對象  
id object = [unarchiver decodeObjectForKey:Key]; //用Key進行解碼  
[unarchiver finishDecoding]; //結束解碼 

(5)有什麼缺點?

          當待存儲的實例具備成百上千個屬性的時候,單純的一個個去序列化屬性值耗時又費力。(固然能夠藉助runtime機制解決這個缺點,MJExtension這個框架就是這個原理) 

 

四、Document沙盒存儲

定義:     

          每一個iOS應用都有本身的應用沙盒(應用沙盒就是文件系統目錄),與其餘文件系統隔離。

          應用必須待在本身的沙盒裏,其餘應用不能訪問該沙盒。

          沙盒的本質就是一個文件夾,名字是隨機分配的。

目錄:

  • Application:存放程序源文件,上架前通過數字簽名,上架後不可修改。
  • Documents: 保存應⽤運行時生成的須要持久化的數據,iTunes同步設備時會備份該目錄。例如,遊戲應用可將遊戲存檔保存在該目錄。
  • tmp: 保存應⽤運行時所需的臨時數據,使⽤完畢後再將相應的文件從該目錄刪除。應用 沒有運行時,系統也可能會清除該目錄下的文件。iTunes同步設備時不會備份該目錄。
  • Library/Caches: 保存應用運行時⽣成的須要持久化的數據,iTunes同步設備時不會備份 該目錄。⼀通常存儲體積大、不須要備份的非重要數據,好比網絡數據緩存存儲到Caches下
  • Library/Preference: 保存應用的全部偏好設置,如iOS的Settings(設置) 應⽤會在該目錄中查找應⽤的設置信息。iTunes同步設備時會備份該目錄。
// 獲取程序的Home目錄       
NSString  *path = NSHomeDirectory();     

// 獲取Document目錄       
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)  fristObject];   
     
// 獲取Cache目錄       
NSString *path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) fristObject];    
     
// 獲取Library目錄       
NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) fristObject]; 

// 獲取Tmp目錄       
NSString *path = NSTemporaryDirectory(); 

做用:

        用來存儲和備份稍微較大的不是很重要的數據,好比緩存圖片、音頻、視頻等,最典型的SDWebImage緩存圖片的框架。

        固然緩存的時間長短根據開發者選擇持久化的目錄路徑有關。

問題:

(1)什麼數據適合存儲?

    圖片、音頻、視頻、文本等

(2)存到什麼地方?

    寫入建立的.txt、.data等任意擴展名的文件中

(3)使用場景?

    當App中涉及到電子書閱讀、聽音樂、看視頻、刷圖片列表等時,推薦使用沙盒存儲。

    由於這能夠極大的節約用戶流量,並且也加強了app的體驗效果。       

(4)如何使用?

          寫入文件

NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)  fristObject];             
NSArray *array = [[NSArray alloc] initWithObjects:@"內容",@"content",nil];            
NSString *filePath = [path stringByAppendingPathComponent:@"testFile.txt"];            
[array writeToFile:filePath atomically:YES];

           讀取文件

NSString *path =  [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) fristObject];            
NSString *filePath = [path stringByAppendingPathComponent:@"testFile.txt"];            
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];

 

五、本地數據庫存儲

定義:

         數據庫是一種經常使用的經過表進行數據存儲的方式,表與表之間能夠有一對1、一對多的聯繫,使用外鍵級聯能夠達到多表存取數據的目的。

         在iOS中,目前有三種熟知數據庫,分別是Sqlite、CoreData、FMDB,用的比較多的是FMDB+Sqlite結合的方式。

特色:

     Sqlite:

  • 是基於c語言開發的數據庫,代碼繁瑣。
  • 用c語言對數據庫執行操做,訪問。
  • sqlite是動態的數據庫類型,即存儲的時候是一種類型,使用的時候能夠存儲爲其餘類型。
  • 在OC中不是可視化,不易理解。
  • 創建鏈接以後能夠不關閉鏈接。

    CoreData;

  • 可視化,且具備undo/redo能力。
  • 能夠實現多種文件格式: * NSSQLiteStoreType 、 * NSBinaryStoreType 、* NSInMemoryStoreType 、* NSXMLStoreTyp。
  • 蘋果官方API支持,與iOS結合更緊密。

    FMDB;

  • FMDB以面向OC的方式封裝了SQLite的C語言API。
  • 使用起來更加面向對象,省去了不少麻煩、冗餘的C語言代碼。
  • 對比蘋果自帶的Core Data框架,更加輕量級和靈活。
  • 提供了多線程安全的數據庫操做方法,有效地防止數據混亂。

做用:

    用來進行大數據量的存儲工做,不只僅是容量大,並且經過索引查找速度很快,在App中這個是基本的功能。 

問題:

(1)什麼數據適合存儲?

    開發者定義的類的實例對象,該對象擁有的屬性能夠是任何類型,例如數字、 字符、 日期、 圖片等等。

(2)存到什麼地方? 

    寫入建立的.sqlite、.db等任意擴展名的文件中 

(3)使用場景?

    在App中有大量的數據在沒有網絡的狀況下仍然須要顯示時,此時推薦使用本地數據庫緩存這些數據。      

(4)如何使用?

      Sqlite:  

  • 新建項目時,先導入系統框架(C語言). (libsqlite3)
  • 頭文件#import<sqlite3.h>
  • sqlite3_open(fileName.UTF8String, &_db); //打開或者建立一個數據 ,*_db本身定義一個sqlite3的成員變量.進行增刪改查時要用
  • sqlite3_exec(_db, sql, NULL, NULL,&error);  //不帶結果集的語句,只是對錶作操做,不會返回出結果,*該函數可進行insert,delete,update操做.
  • sqlite3_prepare_v2(_db, sql, -1, &stmt, NULL);  //作查詢前準備,檢測SQL語句是否正確.(查詢操做select. 帶結果集的查詢語句,會返回出結果,從表中查詢到的數據都會放到stmt結構體中)
  • sqlite3_step(stmt)  //提取查詢到的數據,一次提取一條,經過循環能夠取出全部數據
  • sqlite3_column_text(stmt, 0) //取出第0列的數據.
  • sqlite3_close(sqlite3 *); //關閉數據庫  

  源碼

NSString *sqlStr = @"INSERT OR REPLACE INTO note (cdate,content) VALUES (?,?)";
sqlite3_stmt *statement;
//預處理過程,產生結果集
if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement,
NULL) == SQLITE_OK)
 {
 NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *nsdate = [dateFormatter stringFromDate:model.date];
    //綁定參數開始
    sqlite3_bind_text(statement, 1, [nsdate UTF8String], -1, NULL);  sqlite3_bind_text(statement, 2, [model.content UTF8String],
-1,    NULL);
    //執行插入
if (sqlite3_step(statement) != SQLITE_DONE)
 { 
NSAssert(NO, @"插入數據失敗。"); }
 }
}
//清理結果集,防止內存泄露
sqlite3_finalize(statement);

      CoreData;

  • 建立項目時,勾選CoreData選項。
  • 此時項目文件中多了一個CoreData___.xcdatamodel文件,選中該文件,進入其建立數據庫表格界面,在界面的左下角點擊Add Entity添加實體對象,並設置該對象的類名;與此同時,在AppDeletegate類中,自動聲明和定義了須要的三個對象Managed Object Model,Persistent Store Coordinator,Managed Object Context ,而且自動封裝了大量的sqlite的函數方法。
  • 在attributes選項處添加該實體對象的屬性。
  • 選中該實體類,在模擬器選項上點擊Editor下的create Managed Object Context subclass..建立Managed Object Context的子類。
  • 這個子類中,編譯器自動生成了所添加的全部屬性。
  • 在應用程序代理類中用代碼對數據庫進行操做。 

   源碼

//獲取實體對象
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:@「ClassName」 inManagedObjectContext:self.managedObjectContext];
 
//建立請求對象  
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@「ClassName」]; 
                            
//建立排序對象
NSSortDescriptor *ageSort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:YES]
[request setSortDescriptors:@[ageSort]];
 
//取出全部的數據
NSArray *fetcheObjects = [self.managedObjectContext executeFetchRequest:request error:&error]  

      FMDB:

  • 建立或打開

                             self.db = [FMDatabase databaseWithPath:fileName];  //建立數據庫

                             [self.db open];//打開數據庫

  • 查詢語句:

        - (FMResultSet *)executeQuery:(NSString*)sql, ... //返回結果集   

                             - (BOOL)next; //遍歷

                             - { type }ForColumnIndex:(int)columnIdx  //取出某一行對應的具體數據

  • 建立、插入、修改等等語句:

                       - (BOOL)executeUpdate:(NSString*)sql, ... //執行更新 

  • 關閉數據庫

                             [self.db close]; //關閉數據庫

     源碼

//<1>使用:(須要FMDatabase *db成員變量)
//建立或打開:FMDataBase類
self.db = [FMDatabase databaseWithPath:fileName];
[self.db open];
[self.db executeUpdate:@「CREATE TABLE IF NOT EXISTS t_student (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT,age INTEGER)」];
[self.db executeUpdate:@"INSERT INTO t_student(name , age) VALUES(‘admin’,‘10')];
  
//<2>查詢:FMResultSet類
//1.查詢
FMResultSet *set = [self.db  executeQuery:@"SELECT * FROM t_student;"]; 
 
//2.取出數據 即 {type}ForColumnIndex:
while ([set next])
{     
  //取出姓名
  NSString *name = [set stringForColumnIndex:1];
  //取出年齡
  int age = [set intForColumnIndex:2];
  NSString *name = [set stringForColumn:@"name"];
  int age = [set intForColumn:@"age"];
  NSLog(@"name = %@, age = %d", name, age);
}
 
//<3>關閉數據庫
[self.db close];

 

4、選擇:

既然有這五種存儲方案,那麼在項目中應該選擇哪一種是最佳的方式呢,這就涉及到了下面這個問題了。

  • 何時用?通俗的說,也就是針對某種業務,這五個存儲方式的最佳選擇。

針對上面的這個問題,基本是能夠有四種參考的維度,分別是:

  • 數據量
  • 數據類型
  • 數據載體的形式
  • 數據操做的特色

如今就具體的列表說一下這些區別。

存儲方式
數據量
數據類型
數據載體
數據操做特色
應用示例
plist存儲 適合存儲小數據量並且屬於一類的列表類的數據 只能存儲固定的幾種類型,NSString、NSArray、NSDictionary、NSData、NSDate、NSNumber、Boolean,不能存儲自定義對象 非自定義實例對象、基本數據

直接在可見文件上操做,增刪改查很方便

省市列表、表情列表等

provincecity.plist.zip

偏好存儲 適合存儲小數據量而是通常是配置信息類的數據,有時根據須要也會存儲一些標誌鍵值數據

能夠存儲OC定義的全部數據類型

實例對象、基本數據

 

必須依賴NSUserDefaluts實例對象的實例方法進行存取,過程稍微複雜

App應用程序的信息配置,如版本號、app名稱、用戶權限等

標誌鍵值,如判斷啓動頁、是否自動登陸等

歸檔存儲 適合存儲數據量稍微較大的數據 只能存儲實現了NSCoding協議和NSCopying協議的實例對象類型。 實例對象 必須依賴NSKeyedUnarchiver、NSKeyedArchiver的類方法或者實例方法進行存取,有時可能還會結合NSUserDefaluts偏好,過程比較複雜 用戶的登陸/註冊信息,如帳號、姓名、年齡、學校、省份等
沙盒存儲 適合存儲數據量較大的數據 都是存儲二進制的NSdata類型 文件File 須要依賴文件管理者NSFileManager將文件寫入指定的沙盒目錄下、從該目錄中再讀取文件,過程更復雜一些 圖片、音頻、視頻、文本等,如SDWebImage圖片緩存框架
數據庫存儲 適合存儲大數據量的數據 支持全部的數據類型 實例對象 增刪改查方便、快捷,能夠任意寫sql語句批量處理數據、數據庫升級簡單等 App中用戶瀏覽過的數據列表內容、電子書讀書進度等,基本上大多數app都有本地數據庫緩存
相關文章
相關標籤/搜索