Codable是苹果Swift编程语言标准库中用于数据编解码的一套协议组合,由Encodable与Decodable两个协议通过类型别名组合而成,用于把自定义类型转换为JSON、属性列表(Property List)等外部表示,或从这些外部表示还原为内存中的值。它随Swift 4.0于2017年正式引入,取代了原先依赖NSCoding手工编写编解码代码的做法,编译器可自动合成大部分实现,因而成为Swift生态中网络请求与本地持久化的常用方案。
定义
Codable是Swift标准库中的一个类型别名,其定义为 Codable = Decodable & Encodable,即同时要求类型具备解码与编码两种能力[1]。其中Encodable表示该类型的实例可以写出为外部表示,Decodable表示该类型可以从外部表示中读取并构造实例,二者各自只覆盖单向转换,Codable则把两个方向合并为一个约束[1]。苹果官方文档把它描述为一种能够把自身转换为外部表示、也能从外部表示转换回来的类型。
在使用上,把Codable写在自定义类型的声明之后即表示该类型同时采纳这两个协议;由于它是类型别名,任何同时满足两个协议的类型都会被Codable这一约束接受[1]。Swift标准库还让Optional、Array、Dictionary、Set在元素类型满足条件时以条件遵循的方式自动获得Encodable与Decodable能力,因此嵌套结构通常无需额外处理[2]。
原理
编码与解码由Encoder和Decoder两类协议承担。Encodable要求实现 encode(to:) 方法,把自身的数据写入编码器提供的容器;Decodable要求实现 init(from:) 构造器,从解码器提供的容器中读取数据[3]。容器分为按关键字索引的keyed容器、按顺序索引的unkeyed容器和承载单个值的singleValue容器三种,结构化类型一般在keyed容器中逐属性读写[3]。
属性名与外部表示中的键通过CodingKey协议对应,通常实现为一个名为CodingKeys的枚举,其成员名与属性名一致、原始值给出外部格式中的键名,由此可把Swift风格的下划线命名映射到驼峰命名或相反[4]。只要类型的所有属性都可编码,在声明中写上Codable便可由编译器自动合成CodingKeys以及 init(from:) 与 encode(to:)[3]。
在调用侧,JSONEncoder负责把遵循Codable的值编码为JSON字节数据,JSONDecoder负责把JSON字节数据解码为指定类型;二者的日期字段可通过dateDecodingStrategy或dateEncodingStrategy按ISO 8601、Unix时间戳或自定义格式解析,键名转换策略也可整体设置[5]。属性列表格式则由PropertyListEncoder与PropertyListDecoder处理,它们与JSON的实现方式类似,只是输出为plist[6]。
发展历程
Codable由Swift Archival & Serialization提案(编号SE-0166)确立,该提案在2017年4月完成评审并被接受,评审过程中决定把核心协议下沉到Swift标准库而非留在Foundation中,以便与语言更紧密结合[7]。配套的SE-0167提案定义了JSONEncoder、JSONDecoder以及属性列表编解码器,并在Foundation中补充了编码值无效、类型不匹配、数据损坏、值缺失等错误码,使不同编解码器共享一套错误词汇[6]。
Codable随Swift 4.0正式发布。该版本于2017年9月19日随Xcode 9.0推出,标准库在这一版本中为Optional、Array、Dictionary、Set添加了Encodable与Decodable的条件遵循[8]。在此之前的Swift代码多使用JSONSerialization手工解析字典,或依赖第三方JSON映射库,Codable提供了类型安全、无需额外依赖的原生替代方案[9]。
此后Swift社区持续讨论其替代与改进方案。2025年在Swift官方论坛上出现了关于新一代序列化与反序列化API的设计讨论,目标是在保留易用性的同时提高性能,并计划借助宏来定制自动合成的代码[10]。该讨论指出Codable的部分性能开销来自对存在类型的装箱与动态派发,以及keyed容器需要预先解析负载并分配临时字典[10]。
应用
Codable最常见的用途是处理网络接口返回的JSON数据。应用把服务端响应解码为本地模型类型,再把模型编码后提交给接口,整个过程由JSONDecoder与JSONEncoder完成[4]。把数据保存到磁盘、通过连接传输数据、向API提交数据等任务,都可通过先编码为中间格式再由外部表示承载的方式实现[11]。
在自定义映射方面,当属性名与外部键名不一致时可使用CodingKeys枚举指定映射关系;属性可能缺失时可改用可选类型或调用decodeIfPresent在键不存在或值为空时返回nil[7]。对于结构不完全对应的负载,例如外部数据把地址放在子对象中而本地类型把街道、城市平铺为属性,可以请求nestedContainer取出子对象再逐字段读取[7]。
局限
Codable基于值语义设计,对引用语义的对象图支持有限。带有别名(同一对象被多处引用)的对象图在编解码时会被复制,形成环的对象图则无法解码,因为递归会无限进行下去[12]。因此它并不适合直接替代NSCoding来处理任意的类对象归档场景[12]。
自动合成的解码逻辑要求每个非可选属性都必须在负载中出现,缺失对应键时会抛出keyNotFound错误;负载中多出的字段则会被忽略[7]。此外,属性在声明处写的默认值不会在自动合成的解码流程中生效,因为合成的构造器直接调用decode并要求键存在[13]。数组解码中只要有一个元素格式不符,整个数组的解码就会失败,无法跳过个别脏数据[13]。
受Swift类型系统限制,Any与[String: Any]这类动态类型本身不遵循Decodable,无法直接放入Codable模型中,处理高度动态的负载需要额外的包装类型或自定义实现[13]。编解码时也必须在调用处指定具体类型,只知协议而不知具体类型时无法解码,因为解码器需要知道要构造的实际类型[14]。在性能方面,编解码协议的存在类型开销与keyed容器的预解析分配会产生额外的运行时与内存成本[10]。
参见
-
Encodable —— Codable的组成协议之一,规定类型可以编码为外部表示。
-
Decodable —— Codable的组成协议之一,规定类型可以从外部表示解码而来。
-
JSONEncoder —— Foundation提供的编码器,把遵循Codable的值写成JSON数据。
-
JSONDecoder —— Foundation提供的解码器,把JSON数据还原为遵循Codable的类型。
-
CodingKey —— 用于把类型属性名与外部表示中的键对应起来的协议。
-
NSCoding —— Objective-C时代的归档协议,Codable在Swift中部分取代了它的用途。
参考资料
- Codable | Documentation [引用日期2026-10-14]
- CHANGELOG [引用日期2026-10-14]
- Swift Archival & Serialization [引用日期2026-10-14]
- encoding-and-decoding-custom-types [引用日期2026-10-14]
- name: swift-codable [引用日期2026-10-14]
- Swift Encoders [引用日期2026-10-14]
- swift.org 上的网页 [引用日期2026-10-14]
- softwareheritage.org 上的网页 [引用日期2026-10-14]
- 【IOS】Codable [引用日期2026-10-14]
- mjtsai.com 上的网页 [引用日期2026-10-14]
- 编码和解码自定类型 | Apple Developer Documentation [引用日期2026-10-14]
- Revisions to Can NSCoding and Codable co-exist? [引用日期2026-10-14]
- 类型安全的代价与妥协:从 HandyJSON 内存黑客到现代 Swift JSON 解析的架构演进 [引用日期2026-10-14]
- apple.com 上的网页 [引用日期2026-10-14]