關於數據存儲概念:html
數據結構:ios
存儲方式:git
歸檔:把內存數據轉移到閃存中進行持久化的操做的過程。github
經常使用的數據存儲方案:sql
基本數據類型都支持這個機制,NSUserDefaults 也屬於屬性列表存儲,經常使用數據庫
於存儲配置信息。這個機制能夠將這些對象直接序列化到 plist 文件中,多用數組
於少許數據的存儲,效率較高。緩存
simple code:數據結構
// NSUserDefaults 默認 NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setObject:@"skyming" forKey:@"username"]; [userDefaults setObject:[NSDate date] forKey:@"date"]; [userDefaults setObject:@[@"a1",@"a2"] forKey:@"array"]; [userDefaults setInteger:6174 forKey:@"code"]; NSLog(@"userDefaults: %@",[userDefaults dictionaryRepresentation]); NSString *username = [userDefaults objectForKey:@"username"]; NSDate *date = [userDefaults objectForKey:@"date"]; NSArray *array = [userDefaults objectForKey:@"array"]; NSInteger code = [userDefaults integerForKey:@"code"]; NSLog(@"username: %@ date:%@ \n array: %@ code: %d", username, date, array, code); [userDefaults synchronize]; // NSUserDefaults 自定義 NSUserDefaults *defaults = [[NSUserDefaults alloc] init]; [defaults setPersistentDomain:[NSDictionary dictionaryWithObject:@"Hello" forKey:@"World"] forName:@"com.Sensoro.DefaultsTest"]; [defaults synchronize]; // 數組 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docPath = [paths objectAtIndex:0]; NSString *myFile = [docPath stringByAppendingPathComponent:@"my.list"]; //讀取文件 NSArray *array = [[NSArray alloc] initWithContentsOfFile:myFile]; //操做完若修改了數據則,寫入文件 [array writeToFile:myFile atomically:YES];
simple code架構
#pragma mark - #pragma NSCoding協議實現實現 - (void)encodeWithCoder:(NSCoder *)aCoder { //encoder [aCoder encodeObject:self.username forKey:@"username"]; [aCoder encodeObject:self.password forKey:@"password"]; } - (id)initWithCoder:(NSCoder *)aDecoder { //decoder if (self = [super init]) { self.username = [aDecoder decodeObjectForKey:@"username"]; self.password = [aDecoder decodeObjectForKey:@"password"]; } return self; } #pragma NSCopying協議實現 - (id)copyWithZone:(NSZone *)zone { DSObj *copy = [[[self class] allocWithZone:zone] init]; return copy; } // 讀取 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docPath = [paths objectAtIndex:0]; NSString *myFile = [docPath stringByAppendingPathComponent:@"dsObj.list"]; //寫入歸檔文件 NSMutableData *datawrite = [[NSMutableData alloc] init]; NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:datawrite]; [archiver encodeObject:test forKey:@"data"]; [archiver finishEncoding]; [datawrite writeToFile:myFile atomically:YES]; //讀取歸檔文件 NSData *data = [[NSMutableData alloc] initWithContentsOfFile:myFile]; NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; DSObj *testread = [unarchiver decodeObjectForKey:@"data"]; [unarchiver finishDecoding]; NSLog(@"DSObjRead: %@ %@",[testread username], [testread password]);
當屬性多時總不能一個個的添加吧,我的偏向於使用基於運行時的一個方案:
須要歸檔的對象只須要繼承便可。
連接: https://github.com/skyming/SSObjectBase
iOS的SDK裏預置了SQLite3的庫,開發者能夠自建SQLite數據庫。SQLite每次寫入數據
都會產生IO消耗,把數據歸檔到相應的文件。SQLite擅長處理的數據類型其實與NSUser-
Defaults差很少,也是基礎類型的小數據,只是從組織形式上不一樣。開發者能夠以關係型
數據 庫的方式組織數據,使用SQL DML來管理數據。通常來講應用中的格式化的文本類
數據能夠存放在數據庫中,尤爲是相似聊天記錄、Timeline等這些具備條件查詢和排序需
求的數據。每個數據庫的句柄都會在內存中都會被分配一段緩存,用於提升查詢效率。
另外一個方面,因爲查詢緩存,當產生大量句柄或數據量較大時,會出現緩存過大,形成
內存浪費。
simple code
//open database - (void)openDataBase { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docPath = [paths objectAtIndex:0]; NSString *myFile = [docPath stringByAppendingPathComponent:@"data.db"]; if (sqlite3_open([myFile UTF8String], &database)==SQLITE_OK) { NSLog(@"open sqlite db ok."); } else { NSLog( @"can not open sqlite db " ); //close database sqlite3_close(database); } } //create table - (void)createTable { char *errorMsg; const char *createSql="create table if not exists persons (id integer primary key autoincrement,name text)"; if (sqlite3_exec(database, createSql, NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"create ok."); } else { NSLog( @"can not create table" ); [self ErrorReport:[NSString stringWithFormat:@"%s",createSql]]; } } //insert table - (void)insertTable { char *errorMsg; const char *insertSql="insert into persons (name) values ('skyming')"; if (sqlite3_exec(database, insertSql, NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"insert ok."); } else { NSLog( @"can not insert it to table" ); [self ErrorReport:[NSString stringWithFormat:@"%s",insertSql]]; } } //query table - (void)queryTable { const char *selectSql="select id,name from persons"; sqlite3_stmt *statement; if (sqlite3_prepare_v2(database, selectSql, -1, &statement, nil)==SQLITE_OK) { NSLog(@"select ok."); while (sqlite3_step(statement)==SQLITE_ROW)//SQLITE_OK SQLITE_ROW { int _id=sqlite3_column_int(statement, 0); NSString *name=[[NSString alloc] initWithCString:(char *)sqlite3_column_text(statement, 1) encoding:NSUTF8StringEncoding]; NSLog(@"row>>id %i, name>> %@",_id,name); } } else { //error [self ErrorReport:[NSString stringWithFormat:@"%s",selectSql]]; } sqlite3_finalize(statement); } //delete table - (void)deleteTable { char *errorMsg; [self openDataBase]; const char *sql = "DELETE FROM persons where id=24"; if (sqlite3_exec(database, sql, NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"delete ok."); } else { NSLog( @"can not delete it" ); [self ErrorReport:[NSString stringWithFormat:@"%s",sql]]; } } //error - (void)ErrorReport: (NSString *)item { char *errorMsg; if (sqlite3_exec(database, [item cStringUsingEncoding:NSUTF8StringEncoding], NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"%@ ok.",item); } else { NSLog(@"error: %s",errorMsg); sqlite3_free(errorMsg); } }
SQLite的使用起來要比NSUserDefaults複雜的多,所以建議開發者使用SQLite要
搭配一個操做控件使用,能夠簡化操做。gaosboy 開發的SQLight是一款對SQLit
e操做的封裝,把相對複雜的SQLite命令封裝成對象和方法,能夠供你們參考。
連接地址:https://github.com/gaosboy/SQLight
Github 星比較多的方案:FMDB
連接地址:https://github.com/ccgus/fmdb
官方給出的定義是,一個支持持久化的,對象圖和生命週期的自動化管理方案。嚴格
意義上說CoreData是一個管理方案,他的持久化能夠經過 SQLite、XML或二進制
文件儲存。如官方定義所說,CoreData的做用遠遠不止儲存數據這麼簡單,它能夠
把整個應用中的對象建模並進行自動化的 管理。
上圖,官方文檔中解釋CoreData給出的對象圖示例
正如上圖所示,MyDocument是一個對象實例,有兩個Collection:Employee和
Department,存放各自的對象列表。 MyDocument、Employee和Department
三個對象以及他們之間的關係都經過CoreData建模,並能夠經過save方法進行持久
化。從歸檔文件還原模型時CoreData並非一次性把整個模型中的全部數據都載入
內存,而是根據運行時狀態,把被調用到的對象實例載入內存。框架會自動控制這個
過程,從而達到控制內存消耗,避免浪費。不管從設計原理仍是使用方法上看,Core-
Data都比較複雜。所以,若是僅僅是考慮緩存數據這個需求,CoreData絕對不是一個
優選方案。 CoreData的使用場景在於:整個應用使用CoreData規劃,把應用內的數
據經過CoreData建模,徹底基於CoreData架構應用。蘋果官方給出的一個示例代碼,
結構相對簡單,能夠幫助你們入門CoreData。
連接地址:
以前提到的NSUserDefaults和SQLite適合存儲基礎類型的小數據,而CoreData則不適合
存儲單一的數據,那麼對於相似圖片這 種較大的數據要用什麼方式儲存呢?gaosboy 給出
的建議就是:本身實現一套存儲方案。首先要明確,這個所謂的定製方案適用於互聯網應用
中對遠程數據的緩存,幾個限制條件缺一不可。從需求出發分析緩存數據有哪些要求:按Key
查找,快速讀取,寫入不影響正常操做,不浪費內存,支持歸檔。這些都是基本需求,那麼再
進一步或許還需 要固定緩存項數量,支持隊列緩存,緩存過時等。從這些需求入手設計一個
緩存方案並不十分複雜,Kache是筆者根據開發應用的需求開發的一套緩存組件,通 過度析
Kache但願能夠給你們一個思路。
如上圖所示,Kache扮演的是一個典型緩存角色。應用加載遠程數據生成應用數據
對象的同時,經過Kache把數據緩存起來,再次請求則直接經過Kache獲取數據。
緩存對象能夠是NSDictionary、NSArray、NSSet或NSData這些可直接歸檔的類型,
每一個緩存對象對應一個Key。緩存對象 包括數據和過時時間,內存中存放在一個單例
字典中,閃存中每一個對象存爲一個文件。Key空間按照各類順序存放緩存對象的Key集
合,Pool爲固定大小的 數組,當數量達到上限,最先過時的一個Key將被刪除,對應的
緩存對象也被清除。Queue也是固定大小的數組,以先進先出的規則管理Key的增刪。
每一次有新的緩存對象存入,自動檢測Key空間中過時的集合並清除。此外,控件提供
save和load方法支持持久化和從新載入。Kache最初設計爲存放圖片緩存,以後也曾用
於緩存文本數據,因爲使用了過時和歸檔相結合的邏輯,能夠保證大部分命中的緩存對象
都在內存中,從而獲取了較高的效率。讀者能夠從Github上獲取Kache源碼瞭解更多。
以上介紹了幾種iOS開發中常常會遇到的儲存數據方法,從其存儲原理、使用方式和適用
場景幾方面進行進了簡單的對比。事實上每一款應用都很難採用一種單一的方案完成整個
應用的數據儲存任務,須要根據不一樣的數據類型,選擇最合適的方案,以便整個應用得到
良好的運行時性能。