HandyJSON原理
HandyJSON 的优势
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式, 应用广泛. 在 App 的使用过程中, 服务端给移动端发送的大部分都是 JSON 数据, 移动端需要解析数据才能做进一步的处理. 在解析JSON数据这一块, 目前 Swift 中流行的框架基本上是 SwiftyJSON, ObjectMapper, JSONNeverDie, HandyJSON 这么几种.
我们应该如何选择呢?
首先我们应该先明白解析 JSON 的原理. 目前有两个方向.
保持 JSON 语义, 直接解析 JSON.
SwiftyJSON 就是这样的. 本质上仍然需要根据 JSON 结构去取值.
预定义 Model 类, 将 JSON 反序列化类的实例, 再使用这些实例.
这种方式和 OC 中的 MJExtension 的思路是一致的. 在 Swift 中, ObjectMapper, JSONNeverDie, 以及 HandyJSON 做的都是将 JSON 文本反序列化到 Model 类上.
第二种思路是我们熟悉和比较方便的. 和服务端定义好数据结构, 写好 Model 就可以直接解析.
第二种思路有三种实现方式:
完全沿用 OC 中的方式, 让 Model 类继承自 NSObject, 通过 class_copyPropertyList 方法拿到 Model 的各种属性, 从 JSON 中拿到对应的 value, 再通过 OC 中 利用runtime 机制 实现的 KVC 方法为属性赋值. 如 JSONNeverDie.
支持纯 Swift 类, 但要求开发者实现 mapping 函数, 使用重载的运算符进行赋值, 如 ObjectMapper.
获取到 JSON 数据后, 直接在内存中为实例的属性赋值. HandyJSON 就是这样实现的.
第一种方式的缺点在于需要强制继承 NSObject, 这不适用于 struct 定义的 Model. 因为 struct 创建的 Model 不能通过 KVC 为其赋值.
第二种方式的缺点在于自定义 mapping 函数, 维护比较困难.
第三种方式在使用上和 MJExtension 基本差不多, 比较方便. 是我们所推荐的.
HandyJSON 解析数据的原理.
如何在内存上为实例的属性赋值呢?
为属性赋值, 我们需要以下步骤:
获取到属性的名称和类型.
找到实例在内存中的 headPointer, 通过属性的类型计算内存中的偏移值, 确定属性在内存中的位置.
在内存中为属性赋值.
在 Swift 中实现反射机制的类是 Mirror, 通过 Mirror 类可以获取到类的属性, 但是不能为属性赋值, 它是可读的. 但是我们可以直接在内存中为实例赋值. 这是一种思路. 另外一种思路是不利用 Mirror, 直接在内存中获取到属性的名称和类型, 这也是可以的. 目前 HandyJSON 就是利用的第二种方式.
1. 核心原理:绕过反射,直接操作内存
传统的 JSON 库(如 ObjectMapper)依赖 Swift 的 Mirror 反射机制遍历模型属性,但反射存在性能瓶颈且无法直接修改属性值。HandyJSON 通过以下方式实现高效解析:
a. 利用类型元数据(Type Metadata)
- Swift 编译器会为每个类型生成元数据,包含属性名称、类型、内存偏移量等信息。
- HandyJSON 直接访问这些元数据,获取属性的名称和内存位置,无需通过反射。
- 例如,结构体的属性在内存中是连续排列的,通过元数据可以计算出每个属性的偏移量。
b. 内存拷贝与指针操作
-
通过
UnsafeMutablePointer直接操作模型实例的内存。 -
将 JSON 值转换为目标类型后,直接写入对应的内存地址。
-
示例代码逻辑:
swift
let offset = getPropertyOffset(from: metadata) // 获取属性偏移量 let pointer = instancePointer + offset // 计算属性内存地址 let value = parseJSONValue(...) // 解析 JSON 值 pointer.storeBytes(of: value, as: type) // 直接写入内存
2. 实现步骤详解
a. 类型元数据解析
- 获取类型信息:通过
type(of:)或泛型类型参数获取类型的元数据。 - 解析属性列表:从元数据中提取属性名称、类型、是否为可选类型(
Optional)等信息。 - 处理继承和协议:遍历类型的继承链,确保父类属性也能被正确映射。
b. JSON 到内存的映射
- 解析 JSON:将 JSON 数据转换为字典(
[String: Any])。 - 匹配键与属性:将 JSON 的键与模型属性名匹配(支持自定义键名映射)。
- 类型转换:将 JSON 值转换为目标属性类型(如 String 转 Int、处理嵌套模型等)。
- 内存写入:通过指针将转换后的值写入模型实例的内存。
c. 特殊类型处理
- 可选类型(Optional):根据 JSON 是否存在键值决定是否写入
nil。 - 枚举(Enum):将 JSON 值映射到枚举的
rawValue或关联值。 - 泛型类型:需要特殊处理元数据的动态解析。
HandyJSON和codeble的区别
HandyJSON 和 Codable 都是 Swift 中用于 JSON 解析的库,但它们的底层实现和使用方式存在较大差异。下面从多个方面对比 HandyJSON 和 Codable 的区别:
1. 底层原理
• HandyJSON
• 通过 元数据解析 实现 JSON 与对象的转换。
• 直接操作 类型元数据,并利用 内存操作 进行对象实例化和属性赋值,避免手动映射字段。
• 解析时不依赖 init 方法,而是直接修改对象的内存,使其更灵活。
• Codable
• 基于 Swift 的 Encodable 和 Decodable 协议,结合编译器自动生成的 CodingKeys 来进行编码和解码。
• 依赖 JSONDecoder 和 JSONEncoder 进行序列化和反序列化。
• 需要手动实现 init(from decoder: Decoder) 进行自定义解析(如果字段名不匹配或需要额外处理)。
2. 使用方式
• HandyJSON
import HandyJSONstruct User: HandyJSON {var name: String?var age: Int?
}let jsonString = "{\"name\":\"Tom\",\"age\":25}"
if let user = User.deserialize(from: jsonString) {print(user.name) // 输出: Tom
}
• Codable
import Foundationstruct User: Codable {var name: Stringvar age: Int
}let jsonData = """
{"name": "Tom", "age": 25}
""".data(using: .utf8)!let decoder = JSONDecoder()
if let user = try? decoder.decode(User.self, from: jsonData) {print(user.name) // 输出: Tom
}
3. 是否需要 init 构造方法
• HandyJSON
• 解析时不需要定义 init(),可以直接解析 JSON 并填充到结构体或类中。
• 直接修改对象的内存,不依赖 init(from:)。
• 适用于 struct 和 class,但更适合 class(因为 struct 可能需要 mutating 修饰)。
• Codable
• 依赖 init(from decoder: Decoder) 进行解析,如果字段名不同,需要手动实现 CodingKeys 或 init(from:)。
• 必须使用 init() 构造函数,如果 struct 的属性是 let,则必须提供所有初始值。
4. 字段匹配
• HandyJSON
• 支持隐式映射,如果 JSON 中的字段与模型属性匹配,HandyJSON 自动填充值。
• 支持 mapping 方法 进行字段转换:
struct User: HandyJSON {var name: String?var age: Int?mutating func mapping(mapper: HelpingMapper) {mapper.specify(property: &name, name: "user_name") // JSON "user_name" → name}
}
• Codable
• 默认情况下,JSON 字段名必须与模型属性一致,否则解析会失败。
• 必须手动使用 CodingKeys 进行字段转换:
struct User: Codable {var name: Stringvar age: Intenum CodingKeys: String, CodingKey {case name = "user_name" // JSON "user_name" → namecase age}
}
5. 可选值处理
• HandyJSON
• 支持非必填字段,如果 JSON 中缺少某个字段,解析时不会出错,而是保持默认值 nil。
• 更适合处理 不确定 JSON 结构 的情况。
• Codable
• 字段缺失时解析会失败(除非属性是 Optional)。
• 适用于结构稳定的 JSON,但如果 JSON 结构不固定,需要 init(from:) 进行额外处理。
6. 性能
• HandyJSON
• 由于直接操作 Swift 运行时元数据 和 内存赋值,解析速度快。
• 适用于 大规模数据解析,但由于底层是非官方 API,可能有兼容性风险。
• Codable
• 由于使用 静态类型检查 和 编译期生成的解析代码,解析速度稍慢,但更稳定。
• 适用于 严格类型检查的应用。
7. 适用场景
| 适用场景 | HandyJSON | Codable |
|---|---|---|
| 动态 JSON 结构 | ✅ 更适合 | ❌ 需要手动解析 |
| 稳定 JSON 结构 | ✅ 适用 | ✅ 更适合 |
| 解析速度要求高 | ✅ 较快 | ❌ 相对较慢 |
| 字段匹配灵活 | ✅ 自动映射 | ❌ 需手动 CodingKeys |
| 依赖标准库 | ❌ 依赖 HandyJSON | ✅ 纯 Swift 标准库 |
| 适用于 class | ✅ 更合适 | ✅ 适用 |
| 适用于 struct | ✅ 适用 | ✅ 更推荐 |
8. 总结
| 维度 | HandyJSON | Codable |
|---|---|---|
| 底层原理 | 运行时反射 + 内存操作 | 静态类型解析 + 编译期自动生成 |
| 是否需要 init() | 不需要 | 需要 |
| 自动映射 JSON | ✅ 支持 | ❌ 需 CodingKeys |
| 字段缺失容忍度 | ✅ 可缺失 | ❌ 需 Optional 处理 |
| 性能 | ✅ 高效 | ❌ 稍慢 |
| 兼容性 | ❌ 可能受 Swift 版本影响 | ✅ 稳定 |
| 标准库支持 | ❌ 需引入 HandyJSON | ✅ Swift 原生 |
9. 选择建议
• 如果你的 JSON 结构 稳定,并且 不想依赖第三方库,推荐 Codable。
• 如果你的 JSON 结构 复杂、字段可能缺失,并且 需要高性能解析,推荐 HandyJSON。
• Codable 适用于 Swift 官方标准,更安全、可维护性更高,但对字段要求严格。
• HandyJSON 更灵活,解析速度快,但依赖运行时,可能存在兼容性问题(例如 Swift 版本升级后)。
相关文章:
HandyJSON原理
HandyJSON 的优势 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式, 应用广泛. 在 App 的使用过程中, 服务端给移动端发送的大部分都是 JSON 数据, 移动端需要解析数据才能做进一步的处理. 在解析JSON数据这一块, 目前 Swift 中流行的框架基本上是 SwiftyJSON, …...
SpringBoot+策略模式+枚举类,使用配置文件改进,优雅消除if-else,完全符合OOP原则
需求分析 公司做物联网系统的,使用nettry进行设备连接,对设备进行数据采集,根据设备的协议对数据进行解析,解析完成之后存放数据库,但是不同厂家的设备协议不同。公司系统使用了使用了函数式编程的去写了一个解析类&am…...
[力扣每日一练]关于MySQL和pandas的正则表达式应用
一:题目要求 表:Users-------------------------- | Column Name | Type | -------------------------- | user_id | int | | email | varchar | -------------------------- (user_id) 是这张表的唯一主键。 每一行包含用…...
每日免费分享之精品wordpress主题系列~DAY16
主题介绍: 今日在网上寻找wordpress主题的时候逛到了大叔的网站,赶脚这个主题蛮不错的,于是百度一下,果然,这个主题很受欢迎。作为主题下载站追梦者也不甘落后,马上就发布出来了,希望对你们有用…...
OpenCV图像拼接(9)实现图像拼接功能的一个高级接口cv::Stitcher
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::Stitcher 类是OpenCV中用于实现图像拼接功能的一个高级接口。它简化了从一组部分重叠的图像创建全景图的过程,隐藏了许多底层细节…...
MySQL 用户权限与安全管理
MySQL 用户权限与安全管理 在数据库系统中,用户权限与安全管理是保障数据安全性、完整性和隐私性的核心机制。MySQL 提供了丰富的权限管理功能,可以精细地控制用户对数据库、表以及其他数据库对象的访问权限,同时也支持各种安全管理策略来防…...
dify + deepseek /qwen + win +xinference 等完成知识库建设
Dify.AI The Innovation Engine for Generative AI Applications 简介:Dify是一个用于构建人工智能应用程序的开源平台。我们将后端即服务和LLMOps相结合,简化了生成式人工智能解决方案的开发,使开发人员和非技术创新者都可以使用。CPU>…...
模数转换电路(A/D转换器)
A/D转换,是将输入的模拟电压量转换成相应的数字量。 A/D转换器的类型很多,按工作原理可分为直接转换型和间接转换型两大类。前者直接将模拟电压量转换成数字量,后者是先将模拟电压量转换成一个中间量,再将中间量转换成数字量。 …...
算法 | 麻雀搜索算法原理,公式,改进算法综述,应用场景及matlab完整代码
一、麻雀搜索算法(SSA)原理 1. 算法基础 麻雀搜索算法(Sparrow Search Algorithm, SSA)是2020年提出的一种群体智能优化算法,灵感来源于麻雀群体的觅食与反捕食行为。算法将麻雀分为三类角色:发现者(Producer):适应度最高,负责探索全局最优区域;加入者(Follower)…...
OpenAI深夜直播「偷袭」谷歌!GPT-4o原生图像生成:奥特曼带梗图,AGI战场再燃战火
引言:AI战场的「闪电战」 当谷歌刚刚发布「地表最强」Gemini 2.5 Pro时,OpenAI立即以一场深夜直播「闪电反击」——GPT-4o的原生图像生成功能正式上线!从自拍变梗图到相对论漫画,奥特曼(OpenAI团队)用一连…...
【深度学习】Cross-Attention(交叉注意力)机制详解与应用
Cross-Attention(交叉注意力)机制详解与应用 文章目录 Cross-Attention(交叉注意力)机制详解与应用引言什么是Cross-Attention?Cross-Attention的数学表示Cross-Attention与Self-Attention的区别Cross-Attention的应用…...
《大语言模型赋能证券业开发安全:海云安技术方案在上交所专刊发表》
近日,海云安《大语言模型在证券业开发安全领域的探索与实践》技术方案经过上海证券交易所(以下简称”上交所“)行业专家评审后正式收录于《交易技术前沿——网络安全专刊(2025年第1期 总第61期)》。 证券信息技术研究…...
光流 | 基于光流的人体异常行为检测算法原理,公式,算法改进,matlab代码
===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 人体异常行为检测 一、算法原理与数学模型1. 核心原理2. 关键公式二、算…...
【蓝桥杯】单片机设计与开发,中断系统,外部中断(下)
一、例程一:外部中断执行函数 #include<stc15.h>void main(void) {P20XA0;P00X00;P20X80;P00XFF;IT00;//设置外部中断0;上升沿下降沿均可//IT01;//设置外部中断0;仅下降沿EX01;//允许中断0申请中断EA 1;//打开CPU总中断while(1); }voi…...
【测试工具】如何使用 burp pro 自定义一个拦截器插件
在 Burp Suite 中,你可以使用 Burp Extender 编写自定义拦截器插件,以拦截并修改 HTTP 请求或响应。Burp Suite 支持 Java 和 Python (Jython) 作为扩展开发语言。以下是一个完整的流程,介绍如何创建一个 Burp 插件来拦截请求并进行自定义处理…...
MySQL、创建数据库、表、SQL 函数:数学函数、字符串函数、日期函数、聚合函数
DAY18.1 Java核心基础 MySQL 创建数据库 数据库是一个服务,实际开发需要根据具体的项目创建对应的数据库实例 create database mytest1 default character set utf8 collate utf8_general_ci; create database mytest2 default character set utf8 collate utf8…...
关于我对接了deepseek之后部署到本地将数据存储到mysql的过程
写在前面 今天写一下使用nodejs作为服务端,vue作为客户端,mysql的数据库,对接deepseek的全过程,要实现一个很简单的效果就是,可以自由的询问,然后可以将询问的过程存储到mysql的数据库中。 文档对接 deeps…...
23种设计模式-策略(Strategy)设计模式
策略设计模式 🚩什么是策略设计模式?🚩策略设计模式的特点🚩策略设计模式的结构🚩策略设计模式的优缺点🚩策略设计模式的Java实现🚩代码总结🚩总结 🚩什么是策略设计模式…...
【Exception】MybatisPlusException: can not find lambda cache for this entity
文章目录 环境 | Environment复现步骤 | Reproduction steps报错日志 | Error log源码 | Source CodeUserServiceImpl.javaAddressServiceImpl.javaAbstractSubTableBaseServiceImpl.javaUserEntity.javaAddressEntity.javaSubTableBaseEntity.java 原因分析 | Analysis解决方案…...
vue ts+Windi CSS
1、创建vue项目 trae(字节)打开一个空文件夹 npm install -g vue/cli vue create my-project cd my-project vue add typescript npm run serve vue项目创建完成 2、安装windicss vue add windicss vue.config.js配置 npm install vue-router …...
ffmpeg系列(三)—— 音频重采样
SwrContext 一、SwrContext 的重要字段 SwrContext 是音频重采样的核心配置对象,其关键字段决定了重采样的行为和性能。以下是常用字段及其作用: 字段名称类型作用典型值示例in_sample_rateint输入音频的采样率(Hz)。44100out_…...
Android Gradle 插件(AGP)版本与 Gradle 版本需要严格对应
一、AGP 与 Gradle 版本对照表 Android Gradle 插件版本对应 Gradle 版本适用 Android Studio 版本8.1.x8.2Arctic Fox (2020.3.1+)8.0.x8.0Arctic Fox (2020.3.1+)7.4.x7.5.1IntelliJ IDEA 2022+7.3.x7.4IntelliJ IDEA 2022+7.2.x7.3.3IntelliJ IDEA 2021.3+7.1.x…...
Angular由一个bug说起之十五:自定义基于Overlay的Tooltip
背景 工具提示(tooltip)是一个常见的 UI 组件,用于在用户与页面元素交互时提供额外的信息。由于angular/material/tooltip的matTooltip只能显示纯文本,所以我们可以通过自定义Directive来实现一个灵活且功能丰富的tooltip Overlay…...
使用 Chromedp 监听网页请求和响应
使用 Chromedp 监听网页请求和响应 在进行网络爬虫的时候,有很多网站都有反爬机制,比如你想抓点数据,结果发现每次请求都带一堆奇奇怪怪的参数 —— 什么 timestamp 签名、AES 加密的字段,还有各种 Token 令牌,跟密码…...
Java中用Stream流取出分组后每组最大值对象的ID
取出分组后每组最大值对象的ID 如果只需要获取这些对象的ID(或其他特定字段),而不是整个对象,可以采用以下方法: 方法1:先获取对象再提取ID List<String> customerIds orders.stream().collect(Coll…...
GPT-SoVITS本地部署:低成本实现语音克隆远程生成音频全流程实战
文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 今天要给大家安利一个绝对能让你大呼过瘾的声音黑科技——GPT-SoVITS!这款由花儿不哭大佬精心打造的语音克隆神…...
使用HTTP提交git时,每次都要输入用户名和密码的解决方案
通过https提交的git项目,每次提交都需要输入用户名和密码 解决方案如下: 打开项目目录,通过终端输入: git config --global credential.helper store验证方式: 重启 scode修改or添加文件后使用git上传, 本次需要帐号密码再次修改or添加文件…...
Flutter视频播放优化
在Flutter中实现流畅视频播放,选择合适的播放器插件是关键。推荐使用以下两类插件: 跨平台低延迟方案 flutter_vlc_player:基于VLC引擎,支持RTSP/RTMP协议,通过hwAcc: HwAcc.full启用硬件加速,配合setOpti…...
嵌入式学习第二十八天--顺序栈
栈的基本代码 栈是限定仅在表尾进行插入和删除操作的线性表。 先进后出、后进先出 栈顶:允许操作的一端 栈底:不允许操作的一端 入栈,出栈。 顺序栈 链式栈 302\5 1.创建 CreateSeqStack 2.销毁 DestroySeqStack 3.判断是否为空栈 IsEmptySeqStack 4.判断是否为满…...
基于MCP协议的多模态模型优化在医疗3D打印精密人工关节制造中的研究
一、引言 1.1 研究背景与意义 在全球人口老龄化趋势愈发明显的当下,诸如骨关节炎、类风湿性关节炎这类关节疾病的发病率不断攀升,进而使得人工关节置换手术的需求呈现出激增态势。人工关节置换手术作为治疗终末期关节疾病的有效手段,能够显著缓解患者疼痛,提升关节功能与生…...
