Codable|Codable in Swift
理解代替记忆什么是Codable
Codable is a type alias
typealias Codable = Decodable & Encodable
- 在进行网络传输时,经常会将应用中的model转换为网络传输中的数据格式如json,这叫做编码(Encode);那么接收到网络json数据后,将其转为app中的model则是解码(Decode)
- Swift standard library中定义了几个协议和类来实现编码、解码
- 协议有Encodable、Decodable、Encoder、Decoder
- 类有JSONDecoder、JSONEncoder、PropertyListEncoder、PropertyListDecoder
- 需要编解码的model需要遵循这两个协议
- Encodable有一个方法需要实现--
func encode(to encoder: Encoder) throws
- Decodable的方法是--
init(from decoder: Decoder) throws
根据
JSONDecoder
的源码分析可将大致的编解码过程概括为如下:- 从执行
JSONDecoder
或其他coder的decode/encode
方法开始 - 根据要编解码的类型,分别执行相应类型所遵循的
Decodable
、Encodable
的init
和encode
方法 -
init
和encode
方法内部,通过Decoder
或Encoder
的三个方法(参考下面Decoder小节),获取到要进行编解码的中间数据结构container
- 通过container具体的encode和decode方法将数据从container解码出来,或编码到container中
- 这一步中的
encode
和decode
方法内部,其实是递归的对model的每个property进行编解码,即执行property类型的init
和decode
方法对property编解码 - 每层递归,系统都会根据不同层的CodingKeys数据,对container进行剥离(Decode协议一节有细讲),将剥离后的decoder或encoder,传到下一层的
init
或encode
方法中
- 这一步中的
-
init
和encode
方法结束,编解码过程也就结束了
为了节约开发时间,编译器会根据情况自动添加编解码的代码
比如当一个数据类型遵循
Encodable
或Decodable
时,且该类型的每个property也是codable的
,那编译器会:- 自动添加一个名为
CodingKeys
的实现了CodingKey
协议的enum;enmu的每个case与该类型的每个property一一对应,即默认会为每个property都进行编解码 - 自动实现了
init
和encode
方法
codable
的或者我们不想每个property都去编解码,我们也可以自己实现CodingKey
,而init
和encode
方法系统仍可以自动实现- 这样可以选择对部分属性进行编解码
- 当remote data的key名称和memory data的property名字不同时,可以通过自定义来匹配名称
一句话,支持所有实现很多现有数据类型已经支持该协议Decodable
、Encodable
的数据类型
String
、Int
、Double
、URL
、Data
、Date
等等Decoder协议
Encoder协议也是类似的几个属性和方法
Decoder
和Encoder
出现在前面所说的init
和encode
方法中,它将编解码的数据存放到中间数据结构container中。下面是该协议的属性和方法:- var codingPath: [CodingKey] { get }
- 这个值一般开发者用不到,只有在编解码过程中才会有值,用于编解码时寻找数据的层级
- 出错后,可以使用该值来记录发生错误的key
- var userInfo: [CodingUserInfoKey : Any] { get }
- 不知怎么用
- 【Codable|Codable in Swift】
func container
(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey
func singleValueContainer() throws -> SingleValueDecodingContainer
func unkeyedContainer() throws -> UnkeyedDecodingContainer
container
中取数据或往其中写数据。从decoder的三个方法能够看出(WWDC中也有提到),编解码时支持三种样式数据的编解码。分别是- 形如Dictionary的数据,比如常见的最外层是jsonObject的json数据--对应
container
方法 - 任意一个支持
Decodable
的类型,对应singleValueContainer
方法- 比如下面的json数据是一个表示文本信息的JSONObject,要转成
TextInfo
的model,其中的color这个key对应一个字符串类型,表示一个颜色值{ "color": "(12, 12, 12)", "text" : "I am songgeb!" }
- 我们想把这个颜色值解析成一个自定义的颜色类型比如叫做
MyColor
- 一步步来,我们先在
TextInfo
的init
方法中,通过container
方法获取到container,再执行container的decode方法,指定(keyedBy type: Key.Type) MyColor
类型继续进行解码 - 然后,就来到了
MyColor
的init
方法中,此时的decoder对应的container是剥离后的,只剩下"(12, 12, 12)"
内容。所以可以通过singleValueContainer
方法获取到字符串的值,然后就可以进一步处理了
//TextInfo中 required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.text = try container.decode(String.self, forKey: .text) self.color = try container.decode(MyColor.self, forKey: .color) } //MyColor中 required init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let str = try container.decode(String.self) ... }
- 比如下面的json数据是一个表示文本信息的JSONObject,要转成
- 数组类型(数组的每个元素都是支持
Decodable
),比如jsonArray数据类型。对应unkeyedContainer
方法[ {...}, {...} ]
CodingKey在编解码过程中用于定位要处理的数据
protocol Codingkey {
var stringValue: String { get }
var intValue: Int? { get }
init?(stringValue: String)
init?(intValue: Int)
}
上面我们提到,编解码时支持三种样式的数据类型,一次编解码中,三种样式数据都可能出现,那么CodingKey便用于定位编解码的数据
- 通过协议从属性和方法能看出,CodingKey一定会有一个string值,这很容易理解,对于Dictionary样式的数据,可以用这个string值,结合KVC进行赋值和取值
- intValue则是对应数组类型的数据,因为这种数据没有key,必须通过intValue作为下标,进行取赋值
当前有
JSONDecoder/JSONEncoder
和PlistDecoder/PlistEncoder
两个类型来支持相应数据类型的编解码- 注意,以
JSONDecoder
为例,这里说的Coder与Encoder/Decoder
协议没有任何关系 -
Encoder/Decoder
的对象是和container绑定,也就是和编解码的中间数据绑定的 - 这里说的Coder如
JSONDecoder
则不与任何数据绑定,是独立可复用的编解码器
- json解析时,经常有这种情况,json中用一个字符串表示一个URL,但json中没有URL类型,所以如果遇到空字符串(可能json此时想表示无url值),但系统内部会认为这不是一个合法的URL格式数据,会解析失败
下面的内容时对源码的一点总结,涉及一个知识点--JSONDecoder的decode源码分析type eraser
,但还未写完
- JSONDecoder并不遵循Decoder协议
- JSONDecoder内部通过私有类
_JSONDecoder
进行实际的解码工作,_JSONDecoder
是遵循Decoder协议的 - 解码第一步先用
NSJSONSerialization
将data转为jsonObject - 解码时传入初始化方法
init(from decoder: Decoder)
的参数正是_JSONDecoder
- 通过
func container
方法返回的是(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey KeyedDecodingContainer
类型 -
KeyedDecodingContainer
将各种具体的container(如json)包装到其中
- 定义为,
struct KeyedDecodingContainer
: KeyedDecodingContainerProtocol where K : CodingKey
- typealias KeyedDecodingContainer
.Key = K
- 是从json到object之间的一个存储数据的中间结构
- associatedtype Key
- Encoding and Decoding Custom Types
- Understanding and Extending Swift 4’s Codable
- Swift 4 JSON 解析进阶
推荐阅读
- 深入理解Go之generate
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- 由浅入深理解AOP
- 逻辑回归的理解与python示例
- 「按键精灵安卓版」关于全分辨率脚本的一些理解(非游戏app)
- 深入理解|深入理解 Android 9.0 Crash 机制(二)
- Swift中willSet和didSet的简述
- 不理解句意,你还想做对所有GRE填空题()
- Hacking|Hacking with iOS: SwiftUI Edition - SnowSeeker 项目(一)
- LeetCode算法题-11.|LeetCode算法题-11. 盛最多水的容器(Swift)