iOS数据持久化之二——归档与设计可存储化的数据模型基类

iOS数据持久化之二——归档与设计可存储化的数据模型基类

一、引言

        在上一篇博客中,我们介绍了用plist文件进行数据持久化的方法。虽然简单易用,但随着开发的深入,你会发现,这种方式还是有很大的局限性。试想,如果我们可以将用户的登录返回信息模型,游戏中角色的属性信息模型进行直接的持久化存取,那是不是非常爽的事,幸运的是,我们可以通过归档,来设计一个这样的数据模型。

二、先来精通归档吧

        归档也是iOS提供给开发者的一种数据存储的方式,事实上,几乎所有的数据类型都可以通过归档来进行存取。其存储与读取的过程,主要封装在两个类中:NSKeyedArchiver和NSKeyedUnarchiver。

1、归档的原理

        归档是将一种或者多种数据类型进行序列化,解归档的过程就是将序列化的数据进行反序列化的解码,这里需要注意一点,归档的核心并非是数据的持久化处理,而是数据的序列化处理,持久化的处理依然是通过文件存取来实现的。因此,被归档的数据类型都必须遵守一个相同的协议,才能在这个协议的约束下进行正确的归档与解归档,这个协议就是NSCoding协议,我们可以先来看一下NSCoding中的内容:

?


1

2

3

4

5

6

@protocol NSCoding

 

- (void)encodeWithCoder:(NSCoder *)aCoder;

- (id)initWithCoder:(NSCoder *)aDecoder;

 

@end

这个协议非常简单,一个init的归档方法,一个encode的解归档方法,NSCoder就是归档对象。原则上说,无论是什么数据类型的对象,系统的或者是我们自定义的,都可以通过实现这个协议中的方法来支持归档操作。

2、几种归档与解归档的应用

(1)通过类方法来对rootKey进行归档

        这种方式,我个人理解,很类似于NSUserDefaults中的standardUserDefaults,只是后者是系统为我们创建的一个默认plist文件,而rootKey是系统为我们创建的一个默认的归档键值。说起来比较复杂,举个例子就十分清晰了:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

    NSString *homeDictionary = NSHomeDirectory();//获取根目录

    NSString *homePath  = [homeDictionary stringByAppendingPathComponent:@"atany.archiver"];//添加储存的文件名

    //方式一:通过data数据归档,在将数据写入文件

    NSData *data= [NSKeyedArchiver archivedDataWithRootObject:@"123"];

    [data writeToFile:homePath atomically:YES];

    //方式二:直接写入文件

    [NSKeyedArchiver archiveRootObject:@"456" toFile:homePath];

    //方式一和方式二的效果完全一样 只是解归档的时候不同

     

    //方式一的解归档:先获取data数据,在进行data数据的解归档

    NSLog(@"%@",[NSKeyedUnarchiver unarchiveObjectWithData:data]);

    //方式二的解归档:直接解文件中的归档

    NSLog(@"%@",[NSKeyedUnarchiver unarchiveObjectWithFile:homePath]);

上面的示例是对字符串类型进行的归档,是对单一的数据对象进行的归档,当然,这里的对象是支持数组、字典等集合的,但集合其中的对象,也必须全部支持归档操作。

(2)通过构造新的archiver对象,对多个对象进行归档

        除了上面的类方法,我们还可以自己构造一个归档对象,来对多种不同的对象进行归档:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

    NSString *homeDictionary = NSHomeDirectory();//获取根目录

    NSString *homePath  = [homeDictionary stringByAppendingPathComponent:@"atany.archiver"];//添加储存的文件名

    //这里创建一个可变的data对象作为归档的容器

    NSMutableData * data = [[NSMutableData alloc]init];

    //创建一个归档对象,归档后写入data数据

    NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];

    //对下面的字符串和int值进行归档序列化

    [archiver encodeObject:@"jaki" forKey:@"name"];

    [archiver encodeInt:24 forKey:@"age"];

    //写入data

    [archiver finishEncoding];

    //写入文件

    [data writeToFile:homePath atomically:YES];

     

    //创建解归档的反序列化对象

    NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];

    //进行反序列化

    NSString * name = [unarchiver decodeObjectForKey:@"name"];

    int age = [unarchiver decodeIntForKey:@"age"];

    //打印信息

    NSLog(@"\nname:%@\nage:%d",name,age);

结果如下:

(3)进行自定义对象的归档

        上面介绍中有提到,原则上,任何遵守了NSCoding协议的类都可以进行归档操作,那么对于我们自定义的对象,我们该如何来做呢?

首先,我们新建一个类:

仿照上面的例子,我们写一个这样的类:

?


1

2

3

4

@interface MyObject : NSObject

@property(nonatomic,strong)NSString * name;

@property(nonatomic,assign)int age;

@end

对其进行归档:

?


1

2

3

4

5

6

7

8

    //进行归档

    MyObject * obj = [[MyObject alloc]init];

    obj.name = @"jaki";

    obj.age = 24;

    NSData * data =  [NSKeyedArchiver archivedDataWithRootObject:obj];

    //进行解档

    MyObject * obj2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];

    NSLog(@"\nname:%@\nage:%d",obj2.name,obj2.age);

直接运行,程序会崩溃掉,打印如下:

可以看出,正是我们前边说过的,必须遵守归档协议的对象,才可以被归档,我们在MyObject类中实现如下两个方法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//解档方法

- (instancetype)initWithCoder:(NSCoder *)coder

{

    if (self=[super init]) {

        _name = [coder decodeObjectForKey:@"name"];

        _age = [coder decodeIntForKey:@"age"];

    }

    return self;

}

 

//归档方法

- (void)encodeWithCoder:(NSCoder *)coder

{

    [coder encodeObject:_name forKey:@"name"];

    [coder encodeInt:_age forKey:@"age"];

}

添加了上面两个方法,我们自定义的对象就可以自由归档存取,并可以写入本地,非常cool吧。

三、设计可以归档存取的数据模型基类

1、动机与初衷

        通过上面对归档的介绍,我们可以发现归档一个十分有潜力的应用:可以自由存取自定义的数据对象。这个特性的优势是毫无疑问的,除了可以使我们的数据用起来更加方便,无需多次解析数据外,安全性也更好。但是也带来了一个缺陷,每个类都需要实现NSCoding中的两个方法是十分繁琐的,并且类越复杂,这个步骤越繁琐,如果在之后的修改和优化中类做了改变,相应的方法也要做改变,这将增加很大的工作量并且埋下潜在bug的风险。

        所以我们会想,能否设计一个这样的model基类,来使需要存储的model都继承于它,使我们的model不需要实现NSCoding方法的同时可以支持归档呢,通过runtime和OC语言特性的一些小技巧,我们是可以做到的。

2、基类模型的设计

        我们新建一个BaseModel类,核心方法如下:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

//归档与解归档的方法

- (instancetype)initWithCoder:(NSCoder *)coder

{

    self = [super init];

    if (self) {

        //获取所有属性

        NSArray * porpertyArray = [self getAllPropertys];

        for (NSString * name in porpertyArray) {

            //去掉属性名前面的_

            NSString * key = [name substringFromIndex:1];

            //约定好的键值对 c+key

            [self setValue:[coder decodeObjectForKey:[NSString stringWithFormat:@"c%@",key]] forKey:key];

        }

    }

    return self;

}

- (void)encodeWithCoder:(NSCoder *)coder

{

     

    //获取所有属性

    NSArray * porpertyArray = [self getAllPropertys];

    for (NSString * name in porpertyArray) {

        //去掉属性名前面的_

        NSString * key = [name substringFromIndex:1];

        //约定好的键值对 c+key

        [coder encodeObject:[self valueForKey:key] forKey:[NSString stringWithFormat:@"c%@",key]];

    }

}

//获取model所有属性

-(NSArray *)getAllPropertys{

    NSMutableArray * array = [[NSMutableArray alloc]init];

     

    unsigned int * count = malloc(sizeof(unsigned int));

    //调用runtime的方法

    //Ivar:方法返回的对象内容对象,这里将返回一个Ivar类型的指针

    //class_copyIvarList方法可以捕获到类的所有变量,将变量的数量存在一个unsigned int的指针中

    Ivar * mem = class_copyIvarList([self class], count);

    //进行遍历

    for (int i=0; i< *count ; i++) {

        //通过移动指针进行遍历

        Ivar var = * (mem+i);

        //获取变量的名称

        const char * name = ivar_getName(var);

        NSString * str = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];

        [array addObject:str];

    }

    //释放内存

    free(count);

    //注意处理野指针

    count=nil;

    return array;

}

通过这样的一个runtime机制,我们可以很方便的是新建的model继承于这个基类,无需其他处理直接支持归档,修改与优化都不受影响。

时间: 2024-09-20 00:51:37

iOS数据持久化之二——归档与设计可存储化的数据模型基类的相关文章

iOS数据持久化之一——plist文件

iOS数据持久化之一--plist文件         iOS开发中,我们时常会将一些简单的数据进行持久化的存储,方便我们保存程序的一些配置和用户的一些数据,plist文件就是我们保存这些数据的最佳选择. 一.何为plist         plist是一种文件格式,其内容规则是xml文件,后缀为.plist,因此,我们更习惯于成它问plist文件,在iOS开发中,这种文件常用来保存一些简单的配置数据,例如项目中的info.plist. 通过plist文件编辑器,我们可以很方便的查看和编辑层次清

iOS数据持久化-SQLite数据库使用详解

使用SQLite数据库 创建数据库 创建数据库过程需要3个步骤: 1.使用sqlite3_open函数打开数据库: 2.使用sqlite3_exec函数执行Create Table语句,创建数据库表: 3.使用sqlite3_close函数释放资源. 这个过程中使用了3个SQLite3函数,它们都是纯C语言函数,通过Objective-C去调用C函数当然不是什么问题,但是也要注意Objective-C数据类型与C数据类型兼容性问题. 下面我们使用SQLite技术实现备忘录案例,与属性列表文件实现

iOS学习之数据持久化详解

前言 持久存储是一种非易失性存储,在重启设备时也不会丢失数据.Cocoa框架提供了几种数据持久化机制: 1)属性列表: 2)对象归档: 3)iOS的嵌入式关系数据库SQLite3: 4)Core Data. 在iOS开发中,持久化数据的方法也并不限于属性列表.对象归档.SQLite3和Core Data.它们只是四种最常用且简单的方法.其实也可以使用传统C语言I/O调用(比如,fopen())读写数据,也可以使用Cocoa的底层文件管理工具.只不过这两种方法都需要写很多代码,并且没有必要这么做.

iOS用两行代码完美解决数据持久化_IOS

前言 在实际的iOS开发中,有些时候涉及到将程序的状态保存下来,以便下一次恢复,或者是记录用户的一些喜好和用户的登录信息等等. 这就需要涉及到数据的持久化了,所谓数据持久化就是数据的本地保存,将数据从内存中迁入到存储器上.网上有很多种数据持久化的方法,如实现自己实现I/O.数据库.云或则走第三方接口等等.但是有时候可能只是进行一些简单的数据存储,如用户的偏好设置.用户的sessionID等等,这时候使用上述方法便显得有点兴师动众了,现在需要一种更加轻量化的操作方式. 一.认识 NSUserDef

iOS中 数据持久化 UI高级_17

数据持久化的本质就是把数据由内写到本地(硬盘中),在iOS指将数据写到沙盒文件夹下: 沙盒机制:指的就是采用沙盒文件夹的形式管理应用程序的本地文件,而且沙盒文件夹的名字是随机分配的,采用十六进制方法命名: =======================关于沙盒目录========================== 沙盒内部构造: 测试沙盒: 属性: @interface ViewController () @property (retain, nonatomic) IBOutlet UITex

iOS中几种数据持久化方案:我要永远地记住你!

概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据.在iOS开发中,有很多数据持久化的方案,接下来我将尝试着介绍一下5种方案: plist文件(属性列表) preference(偏好设置) NSKeyedArchiver(归档) SQLite 3 CoreData 沙盒 在介绍各种存储方法之前,有必要说明以下沙盒机制.iOS程序默认情况下只能访问程序自己的目录,这个目录被称为"沙盒". 1.结构 既然沙盒就是一个文件夹,那就看看里面有什么吧

iOS 通过CoreData实现数据持久化

引言: Core Data 是 iOS 3.0 以后引入的数据持久化解决方案,其原理是对SQLite的封装,是开发者不需要接触SQL语句,就可以对数据库进行的操作. 其编码方式和原理结构方面较为特殊,本博文主要介绍在使用Core Data时遇到的各种问题以及对其核心原理进行解释. 参考资料:  1: iOS教程:Core Data数据持久性存储基础教程 http://www.dasheyin.com/ios_jiao_cheng_core_data_shu_ju_chi_jiu_xing_cun

iOS - Swift 数据持久化

1.Sandbox 沙箱 iOS 为每个应用提供了独立的文件空间,一个应用只能直接访问为本应用分配的文件目录,不可以访问其他目录,每个应用自己独立的访问空间被称为该应用的沙盒.也就是说,一个应用与文件系统的交互绝大部分都被限制在它自己的应用沙盒内. 1)在新 App 被安装时,安装器会为应用创建一系列角色不同的容器(container). iOS 8.0 之后,bundle 目录和沙盒目录 (Data) 是分开的.iOS 7.0 及以前版本 bundle 目录和沙盒目录 (Data) 是在一起的

iOS面试题总结 二

1. Object-c的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么? 答:Object-c的类不可以多重继承;可以实现多个接口,通过实现多个接口可以完成C++的多重继承;Category是类别,一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系. 2. #import 跟#include 又什么区别,@class呢, #import<> 跟 #import"&qu