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

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

一、引言

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

二、先来精通归档吧

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

1、归档的原理

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

@protocol NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;

@end

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

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

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

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

    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对象,对多个对象进行归档

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

    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协议的类都可以进行归档操作,那么对于我们自定义的对象,我们该如何来做呢?

首先,我们新建一个类:

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

@interface MyObject : NSObject
@property(nonatomic,strong)NSString * name;
@property(nonatomic,assign)int age;
@end

对其进行归档:

    //进行归档
    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类中实现如下两个方法:

//解档方法
- (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类,核心方法如下:

//归档与解归档的方法
- (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继承于这个基类,无需其他处理直接支持归档,修改与优化都不受影响。

四、为志同道合的朋友分享

这个model集成在了我的一个开源的开发框架中,当然,那里面也综合和许多许多这样方便开发者使用的功能,如果你感兴趣,可以在https://github.com/ZYHshao/YHBaseFoundationTest上面看到。如果你发现了一些bug或者可以添加或者优化的地方,请务必告知我,十分你感谢。QQ:316045346

时间: 2024-12-27 13:55:27

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

IOS数据持久化之归档NSKeyedArchiver

IOS数据持久化的方式分为三种: 属性列表 (自定义的Property List .NSUserDefaults) 归档 (NSKeyedArchiver) 数据库 (SQLite.Core Data.第三方类库等) 下面主要来介绍一个归档NSKeyedArchiver. 归档(又名序列化),把对象转为字节码,以文件的形式存储到磁盘上:程序运行过程中或者当再次重写打开程序的时候,可以通过解归档(反序列化)还原这些对象. 归档方式: 对Foundation框架中对象进行归档 对自定义的内容进行归档

iOS开发笔记-swift实现iOS数据持久化之归档NSKeyedArchiver

IOS数据持久化的方式分为三种: 属性列表 (plist.NSUserDefaults) 归档 (NSKeyedArchiver) 数据库 (SQLite.Core Data.第三方类库等 归档(又名序列化),把对象转为字节码,以文件的形式存储到磁盘上:程序运行过程中或者当再次重写打开程序的时候,可以通过解归档(反序列化)还原这些对象.本文主要介绍swift实现iOS数据归档. 归档Foundation框架对象 func archiveData(){ var path: AnyObject=NS

iOS数据持久化方式分析

iOS数据持久化的方式一般为:plist文件写入.对象归档.SQLite数据库.CoreData. plist文件写入.对象归档一般用于小的数据量. SQLite数据库.CoreData则用于大的数据量. SQLite是一款轻型的数据库,是一种关系型数据库管理系统,他的设计目的是嵌入式设备中使用. SQLite占用资源非常低,非常适合移动设备中使用,而且是开源免费的 SQLite的数据库操作其实和常规的数据库操作流程是一样的: 1.打开数据库 sqlite3_open() 2.准备SQL语句,采

iOS -数据持久化方式-以真实项目讲解

前面已经讲解了SQLite,FMDB以及CoreData的基本操作和代码讲解(CoreData也在不断学习中,上篇博客也会不断更新中).本篇我们将讲述在实际开发中,所使用的iOS数据持久化的方式以及怎么会使用到这些方式,都会以本人实际开发的场景为例,大约需要花10-15分钟,欢迎大家指正. 一.前言 和大家说一个真实故事,前年我去美图面试(当时的技术仅仅是UI和接口的实现,并不注重很多底层实现和很多概念的原理,换句话说,就是真正的码农),过了技术第一轮和第二轮(前两年的也就是问问技术点的实现),

iOS数据持久化存储

本文中的代码托管在github上:https://github.com/WindyShade/DataSaveMethods 相对复杂的App仅靠内存的数据肯定无法满足,数据写磁盘作持久化存储是几乎每个客户端软件都需要做的.简单如"是否第一次打开"的BOOL值,大到游戏的进度和状态等数据,都需要进行本地持久化存储.这些数据的存储本质上就是写磁盘存文件,原始一点可以用iOS本身支持有NSFileManager这样的API,或者干脆C语言fwrite/fread,Cocoa Touch本身

【IOS开发之Objective-C】数据持久化--文件和归档

在OC中数据持久化的方式有多种,例如:写入一个文件再或是归档.写入文件的方式我们一般都可以直接读取文件中的数据.比如我把数据写进一个XX.txt文档里面.只有数据写入成功了我就可以直接读取这个文件了.但是这样对于一些用户关键的数据并不能起到保密的作用,如果需要保密还需要使用归档操作. 文件的操作 //首先声明一个字符串 NSString * str1 = @"123456789"; 方式一 直接使用方法: - (BOOL)writeToFile:(NSString *)path ato

IOS数据持久化之归档NSKeyedArchiver, NSUserDefaults,writeToFile

//2.文件读写 //支持:NSString, NSArray , NSDictionay, NSData //注:集合(NSArray, NSDictionay)中得元素也必须是这四种类型, 才能够进行文件读写 //string文件读写 NSString *string = @"假如给我有索纳塔"; //Document NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUs

iOS数据持久化-OC

沙盒详解 1.IOS沙盒机制 IOS应用程序只能在为该改程序创建的文件系统中读取文件,不可以去其它地方访问,此区域被成为沙盒,所以所有的非代码文件都要保存在此,例如图像,图标,声音,映像,属性列表,文本文件等. 1.1.每个应用程序都有自己的存储空间 1.2.应用程序不能翻过自己的围墙去访问别的存储空间的内容 1.3.应用程序请求的数据都要通过权限检测,假如不符合条件的话,不会被放行. 通过这张图只能从表层上理解sandbox是一种安全体系,应用程序的所有操作都要通过这个体系来执行,其中核心内容

IOS数据持久化的4种方式

9.1 数据持久化概述 9.2 iOS应用程序目录结构 9.3 读写属性列表 9.4 对象归档 9.5 访问SQLite 9.1 数据持久化概述 iOS中可以有四种持久化数据的方式: 属性列表.对象归档.SQLite3和Core Data 9.2 iOS应用程序目录结构 iOS应用程序运行在Mac os模拟器时候,有一下临时目录模拟器3.1.3为例子: /Users/tony/Library/Application Support/iPhone Simulator/3.1.3/Applicati