【iOS】数据持久化(二)之归档和解档(iOS 13以后)

news2024/11/15 20:10:24

在之前介绍的数据存储方法中,不管是NSUserDefaults还是plist文件都不能对自定义对象进行存储,OC提供的解归档恰好解决了这个问题

本片文章对 iOS13 以后的版本 归档和解档 进行介绍。老版本的解归档见这篇文章:【iOS】文件(对象数据)的归档和解档,参考这篇文章对比学习会对解归档有更好的理解

目录

    • 简介
    • 自定义对象的单个对象归档、解档
    • 多个对象解档归档
    • 嵌套类(复合类)
    • 解档 Success!!
    • 注意
    • MJExtension库(JSONModel、YYModel)


简介

在iOS中,对象的序列化和反序列化分别使用NSKeyedArchiverNSKeyedUnarchiver两个类,我们可以把一个类对象进行序列化然后保存到文件中,使用时再读取文件,把内容反序列化出来。这个过程通常也被称为 对象的编码(归档)和解码(解档)

  • 归档 — 将对象以文件(二进制数据)的形式保存到磁盘上中(也称序列化,持久化)
  • 解档 — 使用时从磁盘上读取该文件的保存路径,从而读取文件的内容(也称反序列化)

归档一般保存自定义对象、自定义对象数组,由于自定义对象不具有归档的性质,所以只有遵循了NSCoding协议的类才可以归档
由于决大多数支持存储数据的Foundation和Cocoa Touch类都遵循了NSCoding协议,因此,对于大多数OC提供的类来说,归档相对而言还是比较容易实现的。

对象归档的文件是保密的,在磁盘上无法查看文件中的内容,而属性列表是明文的,可以查看。通过文件归档产生的文件是不可见的,如果打开归档文件的话,内容是乱码的;ta不同于属性列表和plist文件是可见的,正因为不可见的缘故,使得这种持久性的数据保存更有可靠性

自定义对象的单个对象归档、解档

iOS 13中需要支持NSSecureCoding 协议(父协议为NSCoding)才能支持归档
请添加图片描述

  1. 自定义一个Person类并实现NSCoding 协议的方法
@interface Person : NSObject <NSSecureCoding>

@property (nonatomic, copy)NSString* name;
@property (nonatomic, assign)int age;
@property (nonatomic, assign)double weight;

@end

@implementation Person

//NSCoder是一个抽象类
//归档的协议方法
//将归档对象序列化
- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject: self.name forKey: @"name"];
    [coder encodeInt: self.age forKey: @"age"];
    [coder encodeDouble: self.weight forKey: @"weight"];
}

//解档的协议方法
//将解档对象反序列化
- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        self.name = [coder decodeObjectForKey: @"name"];
        self.age = [coder decodeIntForKey: @"age"];
        self.weight = [coder decodeDoubleForKey: @"weight"];
    }
    
    return self;
}

@end

//NSSecureCoding的协议方法
+ (BOOL)supportsSecureCoding {
    return YES;
}

  1. 初始化待归档对象并进行归档

+ (nullable NSData *)archivedDataWithRootObject:(id)object requiringSecureCoding:(BOOL)requiresSecureCoding error:(NSError **)error;

        Person* person = [[Person alloc] init];
        person.name = @"XY";
        person.age = 20;
        person.weight = 125.0;
        
        //归档成二进制数据流
        NSError* error;
        NSData* data1 = [NSKeyedArchiver archivedDataWithRootObject: person requiringSecureCoding: YES error: &error];
        if (error) {
            NSLog(@"归档错误:%@", error);
            return 0;
        }
        //写入指定路径(一般写入到沙盒,这里方便演示存到一个新的文件夹)
        [data1 writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];

Person对象被序列化后就会被保存在下方的文件中,但无法直接打开
请添加图片描述

通过终端命令打开后,可以看到内容是经过加密的,保证了数据的安全性
请添加图片描述

  1. 开始解档

+ (nullable id)unarchivedObjectOfClass:(Class)cls fromData:(NSData *)data error:(NSError **)error;

        //解档此二进制数据
        error = nil;
        NSData* data2 = [NSData dataWithContentsOfFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver"];
        Person* unarchiverPerson = (Person *)[NSKeyedUnarchiver unarchivedObjectOfClass: [Person class] fromData: data2 error: &error];
        if (error) {
            NSLog(@"解档错误:%@", error);
        }
        NSLog(@"unarchiverPerson:%@", unarchiverPerson);

请添加图片描述

多个对象解档归档

将多个对象归档在同一个文件中:

  1. 初始化待归档对象并进行归档
Person* person1 = [[Person alloc] init];
person1.name = @"XY";
person1.age = 20;
person1.weight = 125.0;
Dog* dog1 = [[Dog alloc] init];
dog1.name = @"Bruce";
person1.dog = dog1;     
        
Person* person2 = [[Person alloc] init];
person2.name = @"Jacky";
person2.age = 21;
person2.weight = 130.0;
Dog* dog2 = [[Dog alloc] init];
dog2.name = @"Oudy";
person2.dog = dog2;
        
//创建归档对象
NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO];
        
//进行归档(编码)操作
[archiver encodeObject: person1 forKey: @"personOne"];
[archiver encodeObject: person2 forKey: @"personTwo"];
        
//将归档(序列化)后的数据写入指定文件中
[archiver.encodedData writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];
        
//结束归档
[archiver finishEncoding];
  1. 依次解档
//解档
NSData* data = [NSData dataWithContentsOfFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver"];
NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData: data error: nil];
unarchiver.requiresSecureCoding = NO;
        
Person* unchiverPerson1 = [unarchiver decodeObjectForKey: @"personOne"];
NSLog(@"%@ %d %lf %@", unchiverPerson1.name, unchiverPerson1.age, unchiverPerson1.weight, unchiverPerson1.dog.name);
Person* unchiverPerson2 = [unarchiver decodeObjectForKey: @"personTwo"];
NSLog(@"%@ %d %lf %@", unchiverPerson2.name, unchiverPerson2.age, unchiverPerson2.weight, unchiverPerson2.dog.name);

嵌套类(复合类)

现对于Person类,设置一个自定义对象dog属性,那么这个内层的Dog类也需要实现NSSecureCoding 协议,否则程序会崩溃:

在这里插入图片描述
上面也提到过,OC提供的类(比如这里的name)已经遵循了此协议,因此无需手动操作,但自定义的Dog类要手动添加协议函数:

@interface Dog : NSObject <NSSecureCoding>

@property (nonatomic, strong)NSString* name;

@end

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject: self.name forKey: @"dogName"];
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        self.name = [coder decodeObjectForKey: @"dogName"];
    }
    
    return self;
}

+ (BOOL)supportsSecureCoding {
    return YES;
}

以下是复合类解归档完整代码:

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Dog.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person* person1 = [[Person alloc] init];
        person1.name = @"XY";
        person1.age = 20;
        person1.weight = 125.0;
        Dog* dog1 = [[Dog alloc] init];
        dog1.name = @"Bruce";
        person1.dog = dog1;
        
        
        Person* person2 = [[Person alloc] init];
        person2.name = @"Jacky";
        person2.age = 21;
        person2.weight = 130.0;
        Dog* dog2 = [[Dog alloc] init];
        dog2.name = @"Oudy";
        person2.dog = dog2;
        
        //创建归档对象
        NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding: NO];
        
        //进行归档操作
        [archiver encodeObject: person1 forKey: @"personOne"];
        [archiver encodeObject: person2 forKey: @"personTwo"];
        
        //将归档(序列化)后的数据写入指定文件中
        [archiver.encodedData writeToFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver" atomically: YES];
        
        //结束归档
        [archiver finishEncoding];
        
        
        //解档
        NSData* data = [NSData dataWithContentsOfFile: @"/Users/jakey/Desktop/CS/Xcode/NSKeyedArchiverTest/test.archiver"];
        NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData: data error: nil];
        unarchiver.requiresSecureCoding = NO;
        
        Person* unchiverPerson1 = [unarchiver decodeObjectForKey: @"personOne"];
        NSLog(@"%@ %d %lf %@", unchiverPerson1.name, unchiverPerson1.age, unchiverPerson1.weight, unchiverPerson1.dog.name);
        Person* unchiverPerson2 = [unarchiver decodeObjectForKey: @"personTwo"];
        NSLog(@"%@ %d %lf %@", unchiverPerson2.name, unchiverPerson2.age, unchiverPerson2.weight, unchiverPerson2.dog.name);
    return 0;
}

运行结果如下:

请添加图片描述

解档 Success!!

注意

如果需要归档的类是某个自定义类的子类时,就需要在归档和解档之前实现父类的解档和归档方法:[super encodeWithCoder: coder];[super initWithCoder: coder];


MJExtension库(JSONModel、YYModel)

其实还可以使用MJExtension第三方库实现解归档,这样就可以不用写复杂的NSCoding协议,只需要一行代码调用写好的宏MJExtensionCodingImplementation就可以实现

MJExtension也和JSONModel、YYModel一样,支持 JSON数据<->Model 的转换同时也支持解归档,它们在代码量级上、性能优化上各有优缺点,详见这篇文章:

【YYModel,MJExtension,JSONModel对比】

具体的学习,小编日后了解!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1243912.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用【画图】软件修改图片像素、比例和大小

打开电脑画图软件&#xff0c;点击开始 windows附件 画图 在画图软件里选择需要调整的照片&#xff0c;点击文件 打开 在弹出窗口中选择照片后点击打开 照片在画图软件中打开后&#xff0c;对照片进行调整。按图中顺序进行 确定后照片会根据设定的值自动调整 保存…

关于接口测试自动化的总结与思考!

序 近期看到阿里云性能测试 PTS 接口测试开启免费公测&#xff0c;本着以和大家交流如何实现高效的接口测试为出发点&#xff0c;本文包含了我在接口测试领域的一些方法和心得&#xff0c;希望大家一起讨论和分享&#xff0c;内容包括但不仅限于&#xff1a; 服务端接口测试介…

51单片机IO口的四种工作状态切换

51单片机IO口的四种工作状态切换 1.概述 这篇文章介绍单片机IO引脚的四种工作模式&#xff0c;每个模式都有各自的用武之地&#xff0c;后面在驱动外设硬件时会用它不同的模式。 2.IO口四种工作模式介绍 PnM1PnM0I/O口工作模式00准双向口&#xff1a;灌电流达20mA&#xff…

刷题学习记录(含2023ISCTFweb题的部分知识点)

[SWPUCTF 2021 新生赛]sql 进入环境 查看源码&#xff0c;发现是get传参且参数为wllm fuzz测试&#xff0c;发现空格&#xff0c;&#xff0c;and被过滤了 同样的也可以用python脚本进行fuzz测试 import requests fuzz{length ,,handler,like,select,sleep,database,delete,h…

nginx 模块相关配置及结构理解

文章目录 模块配置结构模块配置指令先看一下 ngx_command_t 结构一个模块配置的demo简单模块配置的案例演示 模块上下文结构模块的定义 模块配置结构 Nginx中每个模块都会提供一些指令&#xff0c;以便于用户通过配置去控制该模块的行为。 Nginx的配置信息分成了几个作用域(sc…

安卓开发——Android Studio常见报错与解决方法

1. No toolchains found in the NDK toolchains folder for ABI with prefix: arm-linux-android 这个错误是由于较新版本的NDK的./toolchains目录中没有arm-linux-androideabi文件&#xff0c;解决办法是从旧的NDK版本里面复制到自己的NDK的版本里面&#xff0c;就可以了。 打…

需求调研计划及用户需求调研单

1.目的 2.概述 3.需求调研计划 3.1调研目的 3.2调研范围 3.2.1.调研的职能范围 3.2.2.调研的业务范围 3.2.3.调研的地点范围 3.3调研方式 3.4调研阶段 3.5具体时间安排 软件开发全文档获取&#xff1a;点我获取 1、需求调研计划 2、用户需求调研单 项目名称 客…

【目标检测】保姆级别教程从零开始实现基于Yolov8的一次性筷子计数

前言 一&#xff0c;环境配置 一&#xff0c;虚拟环境创建 二&#xff0c;安装资源包 前言 最近事情比较少&#xff0c;无意间刷到群聊里分享的基于百度飞浆平台的一次性筷子检测&#xff0c;感觉很有意思&#xff0c;恰巧自己最近在学习Yolov8&#xff0c;于是看看能不能复…

Spring配置其他注解Spring注解的解析原理

Spring配置其他注解 Primary注解用于标注相同类型的Bean优先被使用权&#xff0c;Primary是Spring 3.0引入的&#xff0c;与Component和Bean一起使用&#xff0c;标注该Bean的优先级更高&#xff0c;则在通过类型获取Bean或通过Autowired根据类型进行注入时&#xff0c;会选用优…

Mysql数据库 18.Mysql SQL优化

SQL优化 一、插入优化 多条插入语句&#xff0c;影响执行效率 优化方案 1、批量插入&#xff1a; 在一条insert语句中多条数据&#xff0c;但是如果数据量过大&#xff0c;也不能完全使用一条语句语句&#xff0c;建议数据量为一次性插入1000条以下的数据 如果数据量多大&…

【Axure教程】用中继器制作卡片多条件搜索效果

卡片设计通过提供清晰的信息结构、可视化吸引力、易扩展性和强大的交互性&#xff0c;为用户界面设计带来了许多优势&#xff0c;使得用户能够更轻松地浏览、理解和互动。 那今天就教大家如何用中继器制作卡片的模板&#xff0c;以及完成多条件搜索的效果&#xff0c;我们会以…

12个最佳WordPress投票插件

您是否正在为您的网站寻找WordPress投票插件&#xff1f; WordPress投票插件可让您轻松地在您的网站上进行民意调查&#xff0c;用户可以投票。这是在收集见解的同时建立用户参与度的有效策略。 在本文中&#xff0c;我们精心挑选了最好的WordPress投票插件&#xff0c;可帮助…

【Vue】创建第一个实例

步骤&#xff1a; 1.创建容器 2.引包 3.创建实例 4.添加配置项 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body><!--准备容器 --> <di…

ubuntu搭建phpmyadmin+wordpress

Ubuntu搭建phpmyadmin wordpress Linux系统设置&#xff1a;Ubuntu 22配置apache2搭建phpmyadmin配置Nginx环境&#xff0c;搭建wordpress Linux系统设置&#xff1a;Ubuntu 22 配置apache2 安装apache2 sudo apt -y install apache2设置端口号为8080 sudo vim /etc/apache…

VBA经典应用69例:Find方法的应用

《VBA经典应用69例》&#xff08;版权10178981&#xff09;&#xff0c;是我推出的第九套教程&#xff0c;教程是专门针对初级、中级学员在学习VBA过程中可能遇到的案例展开&#xff0c;这套教程案例众多&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以便…

旅行商问题(枚举,回溯,动态规划,贪心,分支界限)

文章目录 问题描述暴力枚举回溯法动态规划法贪心法分支界限法 问题描述 假设有一个货郎担要拜访n个城市&#xff0c;他必须选择所要走的路程&#xff0c;路程的限制时每个城市只能拜访一次&#xff0c;而且最后要走到原来出发的城市&#xff0c;要求路径长度。 旅行商问题将要…

C语言第二十五弹--打印菱形

C语言打印菱形 思路&#xff1a;想要打印一个菱形&#xff0c;可以分为上下两部分&#xff0c;通过观察可以发现上半部分星号的规律是 1 3 5 7故理解为 2对应行数 1 &#xff0c;空格是4 3 2 1故理解为 行数-对应行数-1。 上半部分代码如下 for (int i 0;i < line;i){//上…

【C/PTA】函数专项练习(四)

本文结合PTA专项练习带领读者掌握函数&#xff0c;刷题为主注释为辅&#xff0c;在代码中理解思路&#xff0c;其它不做过多叙述。 目录 6-1 计算A[n]1/(1 A[n-1])6-2 递归实现顺序输出整数6-3 自然数的位数(递归版)6-4 分治法求解金块问题6-5 汉诺塔6-6 重复显示字符(递归版)…

git 提交成了LFS格式,如何恢复

平常习惯使用sourceTree提交代码&#xff0c;某次打开时弹出了一个【是否要使用LFS提交】的确认弹窗&#xff0c;当时不知道LFS是什么就点了确认&#xff0c;后续提交时代码全变成了这个样子 因为是初始化的项目首次提交&#xff0c;将近四百个文件全被格式化成了这个样子&…

Python 使用tkinter复刻Windows记事本UI和菜单功能(三)

上一篇&#xff1a;Python 使用tkinter复刻Windows记事本UI和菜单功能&#xff08;二&#xff09;-CSDN博客 下一篇&#xff1a;敬请耐心等待&#xff0c;如发现BUG以及建议&#xff0c;请在评论区发表&#xff0c;谢谢&#xff01; 本文章完成了记事本的新建、保存、另存、打…