iOS 运用NSUserDefaults保存数据实现数据缓存

应同学之约,抽个时间,写篇关于 NSUserDefaults 实现保存数据的文章。
确实在项目中,虽然我们数据都是通过 API ,从后台获取,但是很多时候,我们需要做数据的本地缓保,以便实现我们在没有网络的时候可以读取本地数据,而不致于说获取不到数据,整个页面都空白的尴尬。还有如果是已经有本地数据缓保,那么就不用每次都从通过网络从后台获取,可以提高用户体验,也不至于浪费太多用户的流量。我们就先讨论这两种简单的情况,其他特殊情况先排除开来。
那么一般我们接触到的本地存储数据就几种:
1,归档写入沙盒

[str writeToFile:strFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];

2,数据库
可以借助 FMDB 等第三方库去实现

3,NSUserDefaults

当然大神如果有不同见解,欢迎提出。
这三种方法也是各有利弊,至于有什么不同,跟优缺点,大家就自行百度、google吧,这里就不讲太多。
今天我们就主要讲一下第3种,最简单粗暴的方法。

NSUserDefaults是一个单例,在整个程序中只有一个实例对象,而且不同于我们自己定义的单例,因为他不会随着这个程序的关闭而清除掉保存的数据,下次你开启程序,你之前保存的数据还是存在的,所以他可以用于本地数据的持久化,而且简单实用,我们也可以用来做为页面间的传值。
NSUserDefaults支持的数据类型有:NSNumber(NSInteger、float、double),NSString,NSDate,NSArray,NSDictionary,BOOL.
其实他的用法非常简单,跟我们平时使用字典基本一样。一个key,一个Value。
就比如说,我们在做登陆功能的时候,基本上都要记住用户的用户名,下次用户再打开APP的时候,登陆的时候就不用再输入用户名了。保存代码如下:

    //将NSString 对象存储到 NSUserDefaults 中
    NSString *userName = @"Jimmy.Lin";
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:userName forKey:@"saveUserName"];

那么下次打开登陆页面的时候,就可以直接从NSUserDefaults中取出我们所保存的用户名,直接使用了。取出保存的数据代码如下:

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    // 这里用到的 key 就是上次我们保存的时候所使用的 key
    NSString *userName = [ defaults objectForKey:@"saveUserName"];

这里需要注意一点,就是如果你不想数据被覆盖掉,那就需要使用不同的key,换句话讲,就是你现在里面已经用"saveUserName"这个 key 保存了这一次的用户名,如果你再使用同样的这一个 key 保存数据的话,那第二次的 value 会覆盖掉第一次的 value,就是你再次取值的时候,取出来的就是第二次所保存的 value 了。

恩,讲到这里,也许你会在使用过程中,出现你写了保存的代码,但是你再次取值的时候却取不出你之前保存的值,心里估计在骂娘 ,尼玛,苹果这么垃圾,自己的方法都出错,还让我们用。
不用慌,NSUserDefaults偶尔不工作,无法保存改动的数据的原因是,NSUserDefaults的机制是,过一段时间,会自动调用自己的函数synchronize去同步数据的。
而如果你是遇到和我此处类似的,改动数据后,就退出程序了,即在NSUserDefaults还没来得及synchronize之前就退出程序,就需要手动调用synchronize去保存数据了。
也就是说我们可以手动调用NSUserDefaults去执行同步synchronize的动作,以及时保存(修改了的)数据。
那么为了确保万无一失,我们把上面的保存代码完善一下:

//将NSString 对象存储到 NSUserDefaults 中
    NSString *userName = @"Jimmy.Lin";
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:userName forKey:@"saveUserName"];
    // 手动调用NSUserDefaults去执行同步synchronize的动作,以及时保存(修改了的)数据
    [defaults synchronize];

OK,再也不会出现坑爹的保存了数据取不出来值的情况了。

上面讲的是简单的保存简单的字符串,但是一般我们用后台获取到的数据都是 json ,那么我们转化完之后,就是字典或者数组了。那同样的,这些数据也是一样的保存。

    // 保存数组
    NSArray *arr = @[@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9"];

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:arr forKey:@"saveArray"];
    [defaults synchronize];
    // 读取我们保存的数组
    NSArray *getArr = [defaults objectForKey:@"saveArray"];
    // 打印出来看看是否正确
    for (NSString *number in getArr) {
        NSLog(@"number = %@",number);
    }

打印结果如下,正确,perfect


打印结果

保存字典的用法一样,这里就不重复了。

这里同样有一点是需要特别注意的,就是NSUserDefaults保存的数据全是不可变的!!!这一点是非常重要的,弄错的话会有 bug 。到时候别说我是照着这方法保存的,拿出来用,却崩了。真的不是这个方法有问题!

当然如果你只是保存的话是没什么问题的,也不会出现程序崩的情况:


保存可变数组正常

按上面的代码来的话,运行完发现也能拿到我们要的结果,一切正常,那是不是觉得我们拿到的就是我们保存的可变数组?
那我们来验证一下,如果真的是可变数据,那就可以添加删除里面的元素:


添加元素验证

再次运行,发现崩了。看一下程序崩溃原因:


崩溃原因

看到这,很好理解吧。我们拿出来的是一个不可变数组!所以你添加元素的话肯定就崩啦!

那如果我就是非要往拿出来的数组里面添加元素怎么办呢?很简单,先转成可变的,再添加,然后再用同样的 key 保存进去覆盖掉之前的数据就搞定了:


往取出来的数组添加元素

OK,运行一下,打印出来的结果,已经添加进去了:


添加成功

恩。好像用法就是这么简单!看到这里是不是觉得这货不就是一个字典吗?所以你会用字典,这货你也肯定会用 ,so easy !

但是问题又来了,数据拿到的时候是 json ,但是我们基本上都是转换成 model ,然后放进数组里面,这样方便后期处理跟使用。看一下上面讲的 NSUserDefaults 能保存的数据类型,model 明显是不行的。但是别忘了 iOS 强大的数据转化,看一下,NSUserDefaults可以保存 NSData 的数据类型,那么就好办了,只不过是需要多绕一个圈而已。那么怎么来实现这些自定义 model 的数据保存呢?
还是让我们一步一步来吧,首先,那先定义一个 model 类,里面就简单的加三个属性吧,为了方便,我们再添加一个初始化方法:


定义 Model 类

那我们要实现将 model 类转换成 NSData 类型的,就必须归档,这里要实现 在.h 文件中遵循 NSCoding 协议,再 在 .m 中实现 encodeWithCoder 方法 和
initWithCoder 方法就可以了 :


遵循 NSCoding 协议

在 .m 文件里实现我们上面自定义的初始化方法,跟 NSCoding 协议方法:


.m 文件需要实现的方法

这样做就可以将我们自定义的 model 类型转变为 NSData 类型了。
直接上代码吧:


保存自定义的 model 数组

看一下打印结果:


验证结果

好了,到这基本上也没什么好讲的了,都是很简单的用法,至少比第1种,第2种方法使用起来简单太多了!
不过用 NSUserDefaults 保存数据的话,要考虑的问题就是安全性!至于你要用加密还是什么别的方法的话,我在这就不多加讨论了,因为每个公司每个人处理的方法都不一样。

希望大家以后不会再为这简单的数据保存问题头疼了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容