关于数据存储概念:
数据结构:
基本对象:NSDictionary、NSArray和NSSet这些对象。 复杂对象:关系模型、对象图和属性列表多种结构等。存储方式:
内存:内存存储是临时的,运行时有效的,但效率高。闪存:闪存则是一种持久化存储,但产生I/O消耗,效率相对低。归档:把内存数据转移到闪存中进行持久化的操作的过程。
常用的数据存储方案:
属性列表:NSArray、NSDictionary、NSData、NSString、NSNumber、NSDate等基本数据类型都支持这个机制,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:6174forKey:@"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];
对象归档: 自定义对象常用的存储机制,对象必须实现 NSCoding 协议,NSCopying协议可选simple code
#pragmamark -#pragmaNSCoding协议实现实现
- (void)encodeWithCoder:(NSCoder *)aCoder
{//encoder[aCoder encodeObject:self.username forKey:@"username"];
[aCoder encodeObject:self.passWordforKey:@"password"];
}- (id)initWithCoder:(NSCoder *)aDecoder
{//decoderif(self =[super init]) {
self.username= [aDecoder decodeObjectForKey:@"username"];
self.password= [aDecoder decodeObjectForKey:@"password"];
}returnself;
}#pragmaNSCopying协议实现
- (id)copyWithZone:(NSZone *)zone
{
DSObj*copy = [[[selfclass] allocWithZone:zone] init];returncopy;
}//读取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
SQLite: 用于存储查询需求较多的数据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 databasesqlite3_close(database);
}
}//create table- (void)createTable
{char*errorMsg;constchar*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;constchar*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
{constchar*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];constchar*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,用于规划应用中的对象官方给出的定义是,一个支持持久化的,对象图和生命周期的自动化管理方案。严格
意义上说CoreData是一个管理方案,他的持久化可以通过 SQLite、xml或二进制
文件储存。如官方定义所说,CoreData的作用远远不止储存数据这么简单,它可以
把整个应用中的对象建模并进行自动化的 管理。
上图,官方文档中解释CoreData给出的对象图示例
正如上图所示,MyDocument是一个对象实例,有两个Collection:Employee和
Department,存放各自的对象列表。 MyDocument、Employee和Department
三个对象以及他们之间的关系都通过CoreData建模,并可以通过save方法进行持久
化。从归档文件还原模型时CoreData并不是一次性把整个模型中的所有数据都载入
内存,而是根据运行时状态,把被调用到的对象实例载入内存。框架会自动控制这个
过程,从而达到控制内存消耗,避免浪费。无论从设计原理还是使用方法上看,Core-
Data都比较复杂。因此,如果仅仅是考虑缓存数据这个需求,CoreData绝对不是一个
优选方案。 CoreData的使用场景在于:整个应用使用CoreData规划,把应用内的数
据通过CoreData建模,完全基于CoreData架构应用。苹果官方给出的一个示例代码,
结构相对简单,可以帮助大家入门CoreData。
链接地址:
http://developer.apple.com/library/ios/
使用基本对象类型定制的个性化缓存方案之前提到的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开发中经常会遇到的储存数据方法,从其存储原理、使用方式和适用
场景几方面进行进了简单的对比。事实上每一款应用都很难采用一种单一的方案完成整个
应用的数据储存任务,需要根据不同的数据类型,选择最合适的方案,以便整个应用获得
良好的运行时性能。
simpleCode代码
https://github.com/skyming/DataSerializationDemo
相关链接:
http://pingguohe.net/2013/04/02/data-storage-in-ios/
http://blog.csdn.net/nono_love_lilith/article/details/7539659