iOS开发-数据持久化-1

属性列表、? ?NSUserDefault、? ?归档archive、 数据库


属性列表

属性列表是一种明文的轻量级存储方式,其存储格式有多种,最常规格式为XML格式。在我们创建一个新的项目的时候,Xcode会自动生成一个info.plist文件用来存储项目的部分系统设置。plist只能用数组(NSArray)或者字典(NSDictionary)进行读取,由于属性列表本身不加密,所以安全性几乎可以说为零。因为,属性列表正常用于存储少量的并且不重要的数据。

在程序启动后,系统会自动创建一个NSUserDefaults的单例对象,我们可以获取这个单例来存储少量的数据,它会将输出存储在.plist格式的文件中。其优点是像字典一样的赋值方式方便简单,但缺点是无法存储自定义的数据。

数据归档/序列化

与属性列表相反,同样作为轻量级存储的持久化方案,数据归档是进行加密处理的,数据在经过归档处理会转换成二进制数据,所以安全性要远远高于属性列表。另外使用归档方式,我们可以将复杂的对象写入文件中,并且不管添加多少对象,将对象写入磁盘的方式都是一样的。

使用NSKeyedArchiver对自定义的数据进行序列化,并且保存在沙盒目录下。使用这种归档的前提是让存储的数据模型遵守NSCoding协议并且实现其两个协议方法。(当然,如果为了更加安全的存储,也可以遵守NSSecureCoding协议,这是iOS6之后新增的特性)

数据库

sqlite是一个轻量级、跨平台的小型数据库,其拥有可移植性高、有着和MySql几乎相同的数据库语句以及无需服务器即可使用的优点:

一、可以存储大量的数据,存储和检索的速度非??欤欢?、能对数据进行大量的聚合,这样比起使用对象来进行这些操作要快。

当然,它也具有明显的缺点:

一、它没有提供数据库的创建方式;

二、它基于C语言框架设计,没有面向对象的API,所以使用起来比较麻烦;

三、复杂的数据模型的数据建表相对而言比较麻烦。

当然,我们也可以使用基于sqlite封装的开源数据库FMDB来减少使用sqlite的工作量

coreData

coreData是苹果官方iOS5之后推出的综合型数据库,其使用了ORM(Object Relational Mapping)对象关系映射技术,将对象转换成数据,存储在本地数据库中。coreData为了提高效率,甚至将数据存储在不同的数据库中,且在使用的时候将本地数据放到内存中使得访问速度更快。我们可以选择coreData的数据存储方式,包括sqlite、xml等格式。但也正是coreData 是完全面向对象的,其在执行效率上比不上原生的数据库。除此之外,coreData拥有数据验证、undo等其他功能,在功能上是四种持久化方案最多的。

上面已经分别介绍了四种方案的优缺点,在开发中,并没有说哪种持久化方案是最好的,只能说在不同开发场景下,最适合使用的持久化方案。下面我们将用代码实战的方式对这些持久方案进行更加详细的了解



属性列表

在我们每次创建新的项目的时候,Xcode帮助我们生成了Info.plist文件,里面存储了关于项目名字、版本、bundle id等等关键信息,这个plist文件也是逆向工程(越狱)中获取app数据的重要文件。OK,那么什么情况下用plist存储呢?打个比方,最近在实现公司项目业务的时候,需要使用选择器(UIPickerView)给用户选择所在城市。对于城市数据,并没有加密的必要,而且这时候使用plist会达到更高一些的效率。既然已经知道需要的数据,那么很容易就得得出省-市这样的一对多的数据类型,我们的plist使用字典,将省份作为key,存储对应的城市的数组作为value

1、创建plist文件。New Files -> iOS -> Resource -> Property List -> Next

2、给这个plist文件命名cities,点击Create后创建好。然后我们选中Root,默认已经是字典的数据结构存储了,我们点击Root右边的加号添加一些键值对,然后修改左边的key的文字,并且将每一个key对应的value设置为Array数组

3、按照在字典中添加键值对的方式,在设置好每个key对应的类型之后,移动到每一行上面点击出现的+给每一个省份添加数组元素,并且赋值,最终效果图如下


当然,像这种城市的plist文件百度一下就可以找到,但是创建plist的方式相信大家看完之后也就明白了。从plist读取数据的方式也很简单,苹果把读取的方法封装在NSArray跟NSDictionary中,读取步骤分为两步:

1、获取plist文件路径:

NSString * filePath = [[NSBundle mainBundle] pathForResource: @"cities" ofType: @"plist"];

2、通过数组或者字典的构造器方法创建容器:

NSDictionary * dict = [NSDictionary dictionaryWithContentsOfFile: filePath];

//NSArray * array = [NSArray arrayWithContentsOfFile: filePath];

实现选择器的大概思路是用两个数组分别存储省份以及当前选中省份的城市数组,然后在滑动pickerView的回调事件中根据选中的省份更新城市数据源。


NSUserDefault

其支持的数据格式有:NSNumber(Integer、Float、Double等)、NSString、NSDate、NSArray(成员必须也是支持的格式类型)、NSDictionary(同NSArray)。其使用和读取也是非常的简单,像字典一样的存取方式

读?。篘SString * str = [[NSUserDefaults standardUserDefaults] valueForKey: @"str"];

存储:[[NSUserDefaults standardUserDefaults] setValue: @"str" forKey: @"str"];

删除:[[NSUserDefaults standardUserDefaults]? removeObjectForKey:@"str"];

同样的也可以使用setObject:forKey:或者objectForKey:等字典存取方法


数据归档/数据序列化

更多时候,NSUserDefaults已经提供了存储简单变量的持久化方案。然而,当我们想要存储复杂的自定义数据时,NSUserDefaults无法为我们提供更多的帮助,这时候考虑的是另外的持久化方案。而并非所有的程序都需要查询数据、数据迁移这些操作,而且也并非所有的数据都有复杂的关系图。这时候,数据归档绝对是不二的选择。

我们使用archive格式的文件将归档化的数据存储在沙盒目录下,这种格式的文件读取出来是二进制数据(NSData),然后使用NSKeyedUnarchiver类对数据进行反序列化。假设当前需要进行持久化存储的是一款离线游戏,我需要存储游戏前十名的成绩、成绩持有者和记录创建时间这些数据,那么相对应的LXDGameRecord类声明如下,其必须遵循NSCoding或NSSecureCoding协议之一

@interface Model : NSObject <NSCoding>

@property (nonatomic, strong) NSString *title;

@end

@implementation Model

- (instancetype)init{

? ? self = [super init];

? ? if (self) {

? ? ? ? self.title = @"sssss";

? ? }

? ? return self;

}

- (instancetype)initWithCoder:(NSCoder *)coder{

? ? self = [super init];

? ? if (self) {

? ? ? ? self.title = [coder decodeObjectForKey:@"title"];

}

? ? return self;

}

- (void)encodeWithCoder:(nonnull NSCoder *)coder {

? ? [coder encodeObject:self.title forKey:@"title"];

}

@end



1.NSKeyedArchiver归档

archiveRootObject:toFile:在iOS13中已弃用,替换为archivedDataWithRootObject:requiringSecureCoding:error:,可以归档任何类型。

Model *model = [[Model alloc] init];NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model requiringSecureCoding:false error:nil];

2.NSKeyedUnarchiver解档

unarchiveObjectWithFile:同样在iOS13中被弃用,替换为unarchivedObjectOfClass:fromData:error:

Model *model0 = [NSKeyedUnarchiver unarchivedObjectOfClass:[Model class] fromData:data error:nil];

报错:

Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class 'Model' does not adopt it." UserInfo={NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class 'Model' does not adopt it.}

分析错误注意到另一个协议NSSecureCoding

@interface Model : NSObject @property (nonatomic, strong) NSString *title;@end

+ (BOOL)supportsSecureCoding {? ? return YES;}

此处必须返回YES,如果返回NO同样会报错。

Error Domain=NSCocoaErrorDomain Code=4864 "Class 'Model' disallows secure coding. It must return YES from supportsSecureCoding." UserInfo={NSDebugDescription=Class 'Model' disallows secure coding. It must return YES from supportsSecureCoding.}

解档的大坑

如果其中存在NSArray等集合,那么解档同样会失败,不知原因。

@interface Model : NSObject @property (nonatomic, strong) NSArray *title;@end

报错:

Error Domain=NSCocoaErrorDomain Code=4864 "value for key 'title' was of unexpected class 'NSArray'. Allowed classes are '{( Model )}'." UserInfo={NSDebugDescription=value for key 'title' was of unexpected class 'NSArray'. Allowed classes are '{( Model )}'.}

@interface Model : NSObject @property (nonatomic, strong) NSDictionary *title;@end

报错:

Error Domain=NSCocoaErrorDomain Code=4864 "value for key 'title' was of unexpected class 'NSDictionary'. Allowed classes are '{( Model )}'." UserInfo={NSDebugDescription=value for key 'title' was of unexpected class 'NSDictionary'. Allowed classes are '{( Model )}'.}

解决方案

建议转化为JOSN字符串,再进行归档解档

使用其他本地化方案

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

推荐阅读更多精彩内容