ios五种本地缓存数据方式 -尊龙游戏旗舰厅官网
ios本地缓存数据方式有五种:前言
1.直接写文件方式:可以存储的对象有nsstring、nsarray、nsdictionary、nsdata、nsnumber,数据全部存放在一个属性列表文件(*.plist文件)中。
2.nsuserdefaults(偏好设置),用来存储应用设置信息,文件放在perference目录下。
3.归档操作(nskeyedarchiver),不同于前面两种,它可以把自定义对象存放在文件中。
4.coredata:coredata是苹果官方ios5之后推出的综合型数据库,其使用了orm(object relational mapping)对象关系映射技术,将对象转换成数据,存储在本地数据库中。coredata为了提高效率,甚至将数据存储在不同的数据库中,且在使用的时候将本地数据放到内存中使得访问速度更快。我们可以选择coredata的数据存储方式,包括sqlite、xml等格式。但也正是coredata 是完全面向对象的,其在执行效率上比不上原生的数据库。除此之外,coredata拥有数据验证、undo等其他功能,在功能上是几种持久化方案最多的。
5.fmdb:fmdb是ios平台的sqlite数据库框架,fmdb以oc的方式封装了sqlite的c语言api,使用起来更加面向对象,省去了很多麻烦、冗余的c语言代码,对比苹果自带的core data框架,更加轻量级和灵活,提供了多线程安全的数据库操作方法,有效地防止数据混乱。
//获取沙盒中缓存文件夹路径
//方法一
//沙盒主目录
nsstring *homepath = nshomedirectory();
//拼接路径
nsstring *path = [homepath stringbyappendingpathcomponent:@"library/caches"];
//方法二
//第一个参数目标文件夹目录(nscachesdirectory查找缓存文件夹),第二个参数为查找目录的域(nsuserdomainmask为在用户目录下查找),第三个参数为结果中主目录是否展开,不展开则显示为~
nsarray *arr = nssearchpathfordirectoriesindomains(nscachesdirectory, nsuserdomainmask, yes);
//虽然该方法返回的是一个数组,但是由于一个目标文件夹只有一个目录,所以数组中只有一个元素。
nsstring *cachepath = [arr lastobject];
//或者
// nsstring *cachepath = [arr objectatindex:0];
/**
//获取沙盒中document文件夹或者tmp文件夹路径都可使用上面两种方法
//tmp文件夹路径可直接这样获取
nsstring *tmppath = nstemporarydirectory();
nslog(@"%@",tmppath);
**/
//拼接路径(目标路径),这个时候如果目录下不存在这个lotheve.plist文件,这个目录实际上是不存在的。
nsstring *filepath = [cachepath stringbyappendingpathcomponent:@"tese.plist"];
nslog(@"%@",filepath);
//创建数据
nsdictionary *content = @{@"字典数据测试1":@"1",@"字典数据测试2":@"2",@"字典数据测试":@"3"};
//将数据存到目标路径的文件中(这个时候如果该路径下文件不存在将会自动创建)
//用writetofile方法写文件会覆盖掉原来的内容
[content writetofile:filepath atomically:yes];
//读取数据(通过字典的方式读出文件中的内容)
nsdictionary *dic = [nsdictionary dictionarywithcontentsoffile:filepath];
nslog(@"%@",dic);
沙盒中library/caches目录下多了lotheve.plist文件:
文件内容:
达人教路游澳门 【点击进入】 寻找澳门人文踪迹,发现不一样的澳门 北京澳门机票特惠,十月往返2000起 查 看
如何获取模拟器沙盒路径:
打印日志,复制路径打开mac finder,点击左上角菜单前往,前往文件夹,把路径粘贴上去。
nsstring *path = [nssearchpathfordirectoriesindomains(nsdocumentdirectory,nsuserdomainmask,yes) objectatindex:0];
nslog(@"%@",path);
方式二:nsuserdefaults(偏好设置)
每个应用都有一个nsuesrdefaults实例,通过它可以存储应用配置信息以及用户信息,比如保存用户名、密码、字体大小、是否自动登录等等。数据自动保存在沙盒的libarary/preferences目录下。同样,该方法只能存取nsstring、nsarray、nsdictionary、nsdata、nsnumber类型的数据。
属性列表是一种明文的轻量级存储方式,其存储格式有多种,最常规格式为xml格式。在我们创建一个新的项目的时候,xcode会自动生成一个info.plist文件用来存储项目的部分系统设置。plist只能用数组(nsarray)或者字典(nsdictionary)进行读取,由于属性列表本身不加密,所以安全性几乎可以说为零。因为,属性列表正常用于存储少量的并且不重要的数据。
在程序启动后,系统会自动创建一个nsuserdefaults的单例对象,我们可以获取这个单例来存储少量的数据,它会将输出存储在.plist格式的文件中。其优点是像字典一样的赋值方式方便简单,但缺点是无法存储自定义的数据。
代码示例:
//点击button保存数据
- (ibaction)savedata:(id)sender {
//获取nsuserdefaults对象
nsuserdefaults *userdefaults = [nsuserdefaults standarduserdefaults];
//存数据,不需要设置路劲,nsuserdefaults将数据保存在preferences目录下
[userdefaults setobject:@"lotheve" forkey:@"name"];
[userdefaults setobject:@"nsuserdefaults" forkey:@"demo"];
//立刻保存(同步)数据(如果不写这句话,会在将来某个时间点自动将数据保存在preferences目录下)
[userdefaults synchronize];
nslog(@"数据已保存");
}
//点击button读取数据
- (ibaction)getdata:(id)sender
{
//获取nsuserdefaults对象
nsuserdefaults *userdefaults = [nsuserdefaults standarduserdefaults];
//读取数据
nsstring *name = [userdefaults objectforkey:@"name"];
nsstring *demo = [userdefaults objectforkey:@"demo"];
//打印数据
nslog(@"name = %@ demo =%@",name,demo);
}
点击“保存数据”后,查看沙盒中的libarary/ preferences目录:
达人教路游澳门 【点击进入】 寻找澳门人文踪迹,发现不一样的澳门 北京澳门机票特惠,十月往返2000起 查 看
数据以plist的格式写入磁盘中了。点开查看数据:
方式三:nskeyedarchiver(归档操作)
与属性列表相反,同样作为轻量级存储的持久化方案,数据归档是进行加密处理的,数据在经过归档处理会转换成二进制数据,所以安全性要远远高于属性列表。另外使用归档方式,我们可以将复杂的对象写入文件中,并且不管添加多少对象,将对象写入磁盘的方式都是一样的。
使用nskeyedarchiver对自定义的数据进行序列化,并且保存在沙盒目录下。使用这种归档的前提是让存储的数据模型遵守nscoding协议并且实现其两个协议方法。(当然,如果为了更加安全的存储,也可以遵守nssecurecoding协议,这是ios6之后新增的特性)
使用归档操作存储数据的主要好处是,不同于前面两种方法只能存储几个常用的数据类型的数据,nskeyedarchiver可以存储自定义的对象。
代码示例:
先创建一个继承nsobject的类,该类遵守nscoding协议
testperson.h
@interface testperson : nsobject
@property (nonatomic, copy) nsstring *name;
@property (nonatomic, assign) nsinteger age;
@property (nonatomic, copy) nsstring *sex;
@property (nonatomic, strong) nsarray *familymumbers;
@end
testperson.m
#import "testperson.h"
@interface testperson ()
@end
@implementationtestperson
- (void)viewdidload
{
[super viewdidload];
}
#pragma mark - nscoding协议方法 (一定要实现)
//当进行归档操作的时候就会调用该方法
//在该方法中要写清楚要存储对象的哪些属性
- (void)encodewithcoder:(nscoder *)acoder
{
nslog(@"调用了encodewithcoder方法");
[acoder encodeobject:_name forkey:@"name"];
[acoder encodeinteger:_age forkey:@"age"];
[acoder encodeobject:_sex forkey:@"sex"];
[acoder encodeobject:_familymumbers forkey:@"familymumbers"];
}
//当进行解档操作的时候就会调用该方法
//在该方法中要写清楚要提取对象的哪些属性
- (id)initwithcoder:(nscoder *)adecoder
{
nslog(@"调用了initwithcoder方法");
if (self = [super init]) {
self.name = [adecoder decodeobjectforkey:@"name"];
self.age = [adecoder decodeintegerforkey:@"age"];
self.sex = [adecoder decodeobjectforkey:@"sex"];
_familymumbers = [adecoder decodeobjectforkey:@"familymumbers"];
}
return self;
}
@end
这里还要讲一下一个小技巧:使用static修饰来替代宏定义。上面的序列化中,我们可以看到nscoding的协议方法中对数据进行序列化并且使用一个key来保存它。正常情况下我们可以使用宏来定义key,但是过多的宏定义在编译时也会造成大量的损耗。这时候可以使用static定义静态变量来取代宏定义。
static nsstring * const kusernamekey = @"username";
让自定义的数据遵循nscoding协议后,我们就能使用nskeyedarchiver和nskeyedunarchiver来对持久化的数据进行存取操作了:
- (ibaction)savedata:(id)sender
{
//创建一个自定义类的实例
_p = [[testperson alloc]init];
_p.name = @"lotheve";
_p.age = 20;
_p.sex = @"m";
_p.familymumbers = @[@"father",@"mather",@"me"];
//获取文件路径
nsstring *docpath = [nssearchpathfordirectoriesindomains(nsdocumentdirectory, nsuserdomainmask, yes) lastobject];
//文件类型可以随便取,不一定要正确的格式
nsstring *targetpath = [docpath stringbyappendingpathcomponent:@"lotheve.plist"];
//将自定义对象保存在指定路径下
[nskeyedarchiver archiverootobject:_p tofile:targetpath];
nslog(@"文件已储存");
}
- (ibaction)getdata:(id)sender
{
//获取文件路径
nsstring *docpath = [nssearchpathfordirectoriesindomains(nsdocumentdirectory, nsuserdomainmask, yes) lastobject];
nsstring *targetpath = [docpath stringbyappendingpathcomponent:@"lotheve.plist"];
testperson *person = [nskeyedunarchiver unarchiveobjectwithfile:targetpath];
nslog(@"name = %@ , age =%ld , sex = %@ , familymubers = %@",person.name,person.age,person.sex,person.familymumbers);
nslog(@"文件已提取");
}
点击“保存数据”后,查看沙盒中documents目录:
点击查看文件内容:
达人教路游澳门 【点击进入】 寻找澳门人文踪迹,发现不一样的澳门 北京澳门机票特惠,十月往返2000起 查 看
点击“提取数据”后打印结果:
方式四:coredata
coredata是ios5之后苹果推出的数据持久化框架,其提供了orm的功能,将对象和数据相互转换。其中,它提供了包括sqlite、xml、plist等本地存储文件,默认使用sqlite进行存储。coredata具有两个模型:关系模型和对象模型,关系模型即是数据库,对象模型为oc对象。其关系图如下:
由于我们不需要关心数据的存储,coredata使用起来算是最简单的持久化方案。要使用coredata有两个方式,一个是在创建项目的时候勾选use core data,另一个则是手动创建。在这里我们要讲解的是前者创建的方式:
1、创建新项目勾选使用coredata
2、创建关系模型,在这里我创建的模型名字是lxdcoredatademo(注意名字一定要和项目名称保持一致)
3、在创建的关系模型中添加实体,命名为person,并且添加三个字段:name、age、score
到了这里我们的实体模型就创建好了,接下来就是通过nsmanagedobject来将实体模型转换成对象。通过从coredata取出的对象,全部都是继承自nsmanagedobject的子类。那么我们需要根据当前的关系模型来创建person类
选择lxdcoredatademo -> next -> person -> create,我们就创建好了person,这时候三个成员属性都会自动添加完成
在执行操作的类实现文件中,我们要加入appdelegate和person的头文件,因为在创建项目的时候如果我们勾选了use core data的选项,appdelegate文件中会帮我们生成用于管理、存储这些模型的对象,我们可以通过添加头文件来使用。插入数据的代码如下:
//先取出coredata上下文管理者
appdelegate *appdelegate = [[uiapplication sharedapplication] delegate];
nsmanagedobjectcontext *context = appdelegate.managedobjectcontext;
//保存新数据
person *person = [nsentitydescription insertnewobjectforentityforname: @"person" inmanagedobjectcontext: context];
person.name = @"czk"
person.score = [nsnumber numberwithint:100];
person.age = [nsnumber numberwithint:25];
[appdelegate savecontext];
//查询所有数据
nserror *error;
nsfetchrequest *request = [nsfetchrequest new];
nsentitydescription *entity = [nsentitydescription entityforname: @"person" inmanagedobjectcontext: context];
[request setentity: entity];
nsarray *results = [[context executefetchrequest: request error: &error] copy];
for (person *p in results) {
nslog(@"%@, %@, %@", p.name, p.age, p.score);
}
注意:如果出现崩溃异常,请卸载app后重新安装。
方式五:fmdb
优点:
对多线程的并发操作进行处理,所以是线程安全的;
以oc的方式封装了sqlite的c语言api,使用起来更加的方便;
fmdb是轻量级的框架,使用灵活。
缺点:
因为它是oc的语言封装的,只能在ios开发的时候使用,所以在实现跨平台操作的时候存在局限性。
fmdb有三个主要的类
(1)fmdatabase
一个fmdatabase对象就代表一个单独的sqlite数据库
用来执行sql语句
(2)fmresultset
使用fmdatabase执行查询后的结果集
(3)fmdatabasequeue
用于在多线程中执行多个查询或更新,它是线程安全的
这里建议使用cocoapods导入fmdb,关于cocoapods的使用这里就不啰嗦了。
创建数据库:
db=[fmdatabasedatabasewithpath:database_path];
1、当数据库文件不存在时,fmdb会自己创建一个。
2、 如果你传入的参数是空串:@"" ,则fmdb会在临时文件目录下创建这个数据库,数据库断开连接时,数据库文件被删除。
3、如果你传入的参数是 null,则它会建立一个在内存中的数据库,数据库断开连接时,数据库文件被删除。
打开数据库:
[dbopen];
返回bool型。
关闭数据库:
[dbclose];
数据库增删改等操作:
除了查询操作,fmdb数据库操作都执行executeupdate方法,这个方法返回bool型。
看一下例子:
创建表:
if([dbopen]){
nsstring*sqlcreatetable=[nsstringstringwithformat:@"createtableifnotexists'%@'('%@'integerprimarykeyautoincrement,'%@'text,'%@'integer,'%@'text)",tablename,id,name,age,address];
boolres=[dbexecuteupdate:sqlcreatetable];
if(!res){
nslog(@"errorwhencreatingdbtable");
}else{
nslog(@"successtocreatingdbtable");
}
[dbclose];
}
添加数据:
if([dbopen]){
nsstring*insertsql1=[nsstringstringwithformat:
@"insertinto'%@'('%@','%@','%@')values('%@','%@','%@')",
tablename,name,age,address,@"张三",@"13",@"济南"];
boolres=[dbexecuteupdate:insertsql1];
nsstring*insertsql2=[nsstringstringwithformat:
@"insertinto'%@'('%@','%@','%@')values('%@','%@','%@')",
tablename,name,age,address,@"李四",@"12",@"济南"];
boolres2=[dbexecuteupdate:insertsql2];
if(!res){
nslog(@"errorwheninsertdbtable");
}else{
nslog(@"successtoinsertdbtable");
}
[dbclose];
}
修改数据:
if([dbopen]){
nsstring*updatesql=[nsstringstringwithformat:
@"update'%@'set'%@'='%@'where'%@'='%@'",
tablename,age,@"15",age,@"13"];
boolres=[dbexecuteupdate:updatesql];
if(!res){
nslog(@"errorwhenupdatedbtable");
}else{
nslog(@"successtoupdatedbtable");
}
[dbclose];
}
删除数据:
if([dbopen]){
nsstring*deletesql=[nsstringstringwithformat:
@"deletefrom%@where%@='%@'",
tablename,name,@"张三"];
boolres=[dbexecuteupdate:deletesql];
if(!res){
nslog(@"errorwhendeletedbtable");
}else{
nslog(@"successtodeletedbtable");
}
[dbclose];
}
数据库查询操作:
查询操作使用了executequery,并涉及到fmresultset。
if([dbopen]){
nsstring*sql=[nsstringstringwithformat:
@"select*from%@",tablename];
fmresultset*rs=[dbexecutequery:sql];
while([rsnext]){
intid=[rsintforcolumn:id];
nsstring*name=[rsstringforcolumn:name];
nsstring*age=[rsstringforcolumn:age];
nsstring*address=[rsstringforcolumn:address];
nslog(@"id=%d,name=%@,age=%@address=%@",id,name,age,address);
}
[dbclose];
}
fmdb的fmresultset提供了多个方法来获取不同类型的数据:
数据库多线程操作:
如果应用中使用了多线程操作数据库,那么就需要使用fmdatabasequeue来保证线程安全了。 应用中不可在多个线程中共同使用一个fmdatabase对象操作数据库,这样会引起数据库数据混乱。 为了多线程操作数据库安全,fmdb使用了fmdatabasequeue,使用fmdatabasequeue很简单,首先用一个数据库文件地址来初使化fmdatabasequeue,然后就可以将一个闭包(block)传入indatabase方法中。 在闭包中操作数据库,而不直接参与fmdatabase的管理。
fmdatabasequeue * queue = [fmdatabasequeue databasequeuewithpath:database_path];
dispatch_queue_tq1=dispatch_queue_create("queue1",null);
dispatch_queue_tq2=dispatch_queue_create("queue2",null);
dispatch_async(q1,^{
for(inti=0;i<50; i){
[queueindatabase:^(fmdatabase*db2){
nsstring*insertsql1=[nsstringstringwithformat:
@"insertinto'%@'('%@','%@','%@')values(?,?,?)",
tablename,name,age,address];
nsstring*name=[nsstringstringwithformat:@"jack%d",i];
nsstring*age=[nsstringstringwithformat:@"%d",10 i];
boolres=[db2executeupdate:insertsql1,name,age,@"济南"];
if(!res){
nslog(@"errortoinsterdata:%@",name);
}else{
nslog(@"succtoinsterdata:%@",name);
}
}];
}
});
dispatch_async(q2,^{
for(inti=0;i<50; i){
[queueindatabase:^(fmdatabase*db2){
nsstring*insertsql2=[nsstringstringwithformat:
@"insertinto'%@'('%@','%@','%@')values(?,?,?)",
tablename,name,age,address];
nsstring*name=[nsstringstringwithformat:@"lilei%d",i];
nsstring*age=[nsstringstringwithformat:@"%d",10 i];
boolres=[db2executeupdate:insertsql2,name,age,@"北京"];
if(!res){
nslog(@"errortoinsterdata:%@",name);
}else{
nslog(@"succtoinsterdata:%@",name);
}
}];
}
});
总结:
上面已经分别介绍了五种方案的优缺点,在开发中,并没有说哪种持久化方案是最好的,只能说在不同开发场景下,最适合使用的持久化方案。
总结
以上是尊龙游戏旗舰厅官网为你收集整理的ios五种本地缓存数据方式的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇:
- 下一篇: javascript数组查重方法总结