美文网首页Objective-C成长之路
关于归档/解档(序列化/反序列化)

关于归档/解档(序列化/反序列化)

作者: 程序猿界的cai渣渣 | 来源:发表于2017-03-02 21:00 被阅读30次

在昨天看了一个视频,讲解的是归档和解档,也就是“序列化”和“反序列化”。觉得自己所学知识真是太少了!!底层的东西懂得几乎为zero。看了就要记下来,就要去理解,去消化,这是学习,成长的必经之路。虽然辛苦,但是值得。因为,在这其中你可以有很多的乐趣,可以沉浸在里面,感觉很舒服。。。
言归正传:你知道数据持久化的方式有哪几种吗?你猜。。

什么是Serialization (序列化)?

写数据到本地磁盘或者进行传输时,需要进行序列化,转化成二进制流,从而便于传输和存储。同理,得到二进制流后,需要进行反序列化,还原成可以使用的数据。
需要注意的是,不同的环境,serialize和unserialize是不同的。一般同一环境中的操作才能得到正确的数据。

接下来是进行归档的代码:

友情提示:请同学们导入头文件 <objc/message.h>!!!

归档的代码:

//归档
[NSKeyedArchiver archiveRootObject:@"归档的内容" toFile:filePath];  
/*归档的内容可以是任意格式和内容*/

还需要给归档的内容一个存储的路径,另外还可以添加一个后缀名,比如:.txt、.exe、.han、.我的、.dog、.person。。。。就是说,后缀随便自己设置即可(是不是挺好滴呢,想说没说的都可以加上,哼哼)
创建文件并归档:外部设置

Dog *dog = [[Dog alloc] init];
dog.color = @"黑色";
dog.age = 3;
//文件路径:保存一个临时文件
NSString *tempPath = NSTemporaryDirectory();
//后缀名给了一个.dog
NSString *filePath = [tempPath stringByAppendingPathComponent:@"myDog.dog"];
//归档
[NSKeyedArchiver archiveRootObject:dog toFile:filePath];

到这里,有同学就问了:NSTemporaryDirectory()这个方法干甚用滴?创建的是临时文件!!!

获取路径的代码可以直接在viewDIdLoad里面就可以了,代码:

//打印输出临时文件路径
NSLog(@"%@", NSTemporaryDirectory());

给同学们看一下返回的路径(模拟器上):


临时文件路径
那么 stringByAppendingPathComponent 为什么要选择这个呢?当然是为了偷懒不用添加"/"了,哼哼。。
现在你就可以去访问文件了 command+shift+G ,粘贴路径,回车!!!别跟我说你这都不会!你创建的这个文件,什么工具都打不开,只有你自己可以打开:进行 解档(反序列化)。
文件

我创建了一个Dog类,并且赋予它两个属性:

//年龄
@property (nonatomic, assign) NSInteger age;
//颜色
@property (nonatomic, copy) NSString *color;

在外部调用的调用的时候就比较简单了:
ViewController.m中代码:归档(序列化)的已经在上面写过了,接下来是解档(反序列化):外部调用

//文件路径:保存一个临时文件,解档时候当然要获取文件啦
NSString *tempPath = NSTemporaryDirectory();
NSString *filePath = [tempPath stringByAppendingPathComponent:@"myDog.dog"];
//解档
Dog *dog = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(@"我的小狗今年%ld岁,它毛是%@的", (long)dog.age, dog.color);

输出的结果(有图有真相):

解档后
那么问题来了:在Dog类里面是怎么写的呢?问得好!请同学们看下面的精彩之处!!
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeInteger:_age forKey:@"age"];
    [aCoder encodeObject:_color forKey:@"color"];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        _color = [aDecoder decodeObjectForKey:@"color"];
        _age = [aDecoder decodeIntegerForKey:@"age"];
    }
    return self;
}
是否有同学又要问了:如果这只小狗比较牛B,有10个、20个属性呢?难道要这样写20几个归档解档代码吗?太low了吧?问得好!那么怎么解决该问题呢?用 RunTime!!!对!你没听错,是他,是他,就是他!!
归档:Dog.m
- (void)encodeWithCoder:(NSCoder *)aCoder {
    //count是什么意思呢?
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([Dog class], &count);
    for (int i = 0; i < count; i ++) {
        //所有的成员属性
        Ivar ivar = ivars[i];
        //那都属性的名称
        const char *name = ivar_getName(ivar);
        //设置key
        NSString *key = [NSString stringWithUTF8String:name];
        //通过KVC归档
        [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
    //class_copyIvarList是c语言,需要手动管理内存。
    free(ivars);
}
下面对代码解析一下:
(1)class_copyIvarList(<#__unsafe_unretained Class cls#> , <#unsigned int ***outCount#> )

获取某类的所有属性列表,包括@property{ } 中设置的属性。顺便说一下: class_copyPropertyList 则是只有 @property 声明的属性。

点进去之后的方法显示:里面的参数type需要在外面进行设置。

(2)OBJC_EXPORT Ivar ***class_copyIvarList(Class cls, unsigned int *outCount)

返回的类型是:Ivar*,即用“Ivar *”接受数组(属性列表)。
看一下count的类型:unsigned int * outCount,获取到的是属性的个数。unsigned int count = 0;

//下面的打印输出属性个数:
class_copyIvarList(NSClassFromString(@"Dog"), &count);
NSLog(@"Dog属性个数:%d", count);
(3) Ivar ivar = ivars[i];遍历数组中的ivar,为何没有 * 了呢?附加一篇:Ivar

**typedef struct objc_ivar *Ivar; **这是一个结构体!!!

(4)const char *name = ivar_getName(ivar);

请看过OBJC_EXPORT const char *ivar_getName(Ivar v),里面的参数就是没有 * 的哦。获取的是每一个属性。
我们都明白的一个事情是:归档之后就是UTF8格式的啦,获取属性就相当于其中的key。

(4)我们找到了key,那么value不就好弄了吗,通过KVC,欧耶,归档成功!!!!

注意:一定要释放!!因为他是C,手动管理是必须的。 free(ivars);

解档:Dog.m
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
       //count是什么意思呢?
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([Dog class], &count);
        for (int i = 0; i < count; i ++) {
            //所有的成员属性
            Ivar ivar = ivars[i];
            //那都属性的名称
            const char *name = ivar_getName(ivar);
            //设置key
            NSString *key = [NSString stringWithUTF8String:name];
            //利用KVC解档
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        //class_copyIvarList是c语言,需要手动管理内存。
        free(ivars);
    }
    return self;
}

解档没有什么可说的,跟上面归档的节奏基本是一样的,就是记得解档之后的类型是任意的 id 类型,然后利用KVC进行赋值。[self setValue:value forKey:key];即可将其反序列化。
我们使用这些东西可以干什么呢?答:建模型!没错,创建Model。

Runtime的特点是什么?附上一个链接:RunTime

(1)动态创建类。
(2)动态添加属性、方法。
(3)消息机制。

Dog *dog = [[Dog alloc] init];
//一个food方法
[dog foods];
[dog performSelector:@selector(speed) withObject:nil];
//方法名
NSLog(@"%s", __func__);
//消息传递机制
objc_msgSend(dog, @selector(speed));
//消息传递机制
objc_msgSend(dog, sel_registerName("foods"));

以上是简单的对RunTime的使用和理解。以上的链接可以有所帮助。

相关文章

  • IOS本地存储的四种方式

    概要 一、NSKeyedArchiver归档(NSCoding)序列化 需要归档解档的类需要遵守NSCoding协...

  • iOS归档解档

    归档与解档是iOS中序列化与反序列化的方式,需要实现 encodeWithCoder 和 initWithCode...

  • iOS归档和解档

    OC的序列化和反序列化就是用来存储对象和访问对象。序列化就是通过归档把对象转化成二进制文件。反序列化就是通过解档把...

  • 归档 & 解档

    1.什么是 归档 和 解档 数据本地存储持久化的一种。归档:对象的序列化,通过某种格式把对象保存成本地文件。解档:...

  • iOS - 对象的归档与解档(运行时实现)

    归档与解档是IOS中一种序列化与反序列化的方式。对象要实现序列化需要遵循NSCoding协议,而绝大多数Found...

  • IOS 对象的归档与解档

    归档与解档是IOS中一种序列化与反序列化的方式。对象要实现序列化需要遵循NSCoding协议,而绝大多数Found...

  • iOS 归档和解归档

    归档与解档是IOS中一种序列化与反序列化的方式。对象要实现序列化需要遵循NSCoding协议,而绝大多数Found...

  • iOS对象的归档与解档

    归档与解档是IOS中一种序列化与反序列化的方式。对象要实现序列化需要遵循NSCoding协议,而绝大多数Found...

  • 2018-03-12

    ios通过Runtime动态完成数据存储(归档/解档) 实现数据序列化和反序列化,必定实现的两个方法: 当属性比较...

  • IOSRunTime_OC的序列化

    利用RunTime运行时,遍历一个类的所有属性和方法。 序列化-----归档 反序列化--解档 在iOS中一个自定...

网友评论

    本文标题:关于归档/解档(序列化/反序列化)

    本文链接:https://www.haomeiwen.com/subject/slbcgttx.html