iOS: 數據持久化方案


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

 

一、功能:

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

 

二、種類:

     plist存儲:使用XML鍵值對持久化,它適用於少量且數據基本不怎么改變的情況。

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

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

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

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

 

三、詳解:

1、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的數據.這樣很費內存和時間。

 

2、偏好存儲

定義:

  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可以保存和讀取基本的數據類型。

 

3、歸檔序列化存儲

定義:

         對象歸檔是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這個框架就是這個原理) 

 

4、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];

 

5、本地數據庫存儲

定義:

         數據庫是一種常用的通過表進行數據存儲的方式,表與表之間可以有一對一、一對多的聯系,使用外鍵級聯可以達到多表存取數據的目的。

         在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];

 

四、選擇:

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

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

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

  • 數據量
  • 數據類型
  • 數據載體的形式
  • 數據操作的特點

現在就具體的列表說一下這些區別。

存儲方式
數據量
數據類型
數據載體
數據操作特點
應用示例
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都有本地數據庫緩存


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM