文章目录
Codable可以将自身与外部表示形式(例如JSON)进行互相转换的类型。
public protocol Encodable {
func encode(to encoder: Encoder) throws
}
public protocol Decodable {
init(from decoder: Decoder) throws
}
public typealias Codable = Decodable & Encodable
从上可以看到Codable是Decodable和Encodable协议组合的别名
。在这两个协议方法中主要涉及Decoder和Encoder两个协议。
解码
public protocol Decoder {
var codingPath: [CodingKey] { get } //CodingKey协议值可为Int或String
var userInfo: [CodingUserInfoKey : Any] { get } //CodingUserInfoKey协议遵守RawRepresentable、Equatable、Hashable协议
func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey //字典形式
func unkeyedContainer() throws -> UnkeyedDecodingContainer //数组形式
func singleValueContainer() throws -> SingleValueDecodingContainer //原始单值
}
这里来看下KeyedDecodingContainer
public protocol KeyedDecodingContainerProtocol {
associatedtype Key : CodingKey
var codingPath: [CodingKey] { get }
var allKeys: [Self.Key] { get }
func contains(_ key: Self.Key) -> Bool
func decode(.............) //代指decode相关方法
}
public struct KeyedDecodingContainer<K> : KeyedDecodingContainerProtocol where K : CodingKey {
public typealias Key = K
public init<Container>(_ container: Container) where K == Container.Key, Container : KeyedDecodingContainerProtocol
public var codingPath: [CodingKey] { get }
public var allKeys: [KeyedDecodingContainer<K>.Key] { get }
public func contains(_ key: KeyedDecodingContainer<K>.Key) -> Bool
func decode(.............) //代指decode相关方法
}
到这里Decoder协议虽然展示了,但是都用来做什么的?下面通过JSONDecoder
来看一下具体的应用。
struct User: Decodable {
let id: String
var name: String
}
let jsonStub = """
{
"id": "101",
"name": "GC"
}
"""
let jsonData = jsonStub.data(using: .utf8)
let decoder = JSONDecoder()
_ = try? decoder.decode(User.self, from: jsonData!)
JSONDecoder源码解析
open class JSONDecoder {
open var dateDecodingStrategy: DateDecodingStrategy = .deferredToDate //date默认格式
open var dataDecodingStrategy: DataDecodingStrategy = .base64 //data默认格式
open var nonConformingFloatDecodingStrategy: NonConformingFloatDecodingStrategy = .throw //非有限Float、NaN默认策略
open var keyDecodingStrategy: KeyDecodingStrategy = .useDefaultKeys //键默认策略
open var userInfo: [CodingUserInfoKey : Any] = [:]
//decode的type要遵循Decodable协议
open func decode<T : Decodable>(_ type: T.Type, from data: Data) throws -> T {
let topLevel: Any
do {
topLevel = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
} catch {
throw ... //给定的数据不是有效的JSON。
}
let decoder = _JSONDecoder(referencing: topLevel, options: self.options)
guard let value = try decoder.unbox(topLevel, as: type) else {
throw ... //给定的数据不包含top-level值
}
return value
}
......
}
在decode()
方法中
- 先是通过
JSONSerialization.jsonObject(with:options:)
进行了数据序列化 - 然后通过
_JSONDecoder(referencing:options)
创建了一个遵循Decoder
协议的_JSONDecoder
类对象decoder - 然后调用它的
unbox(_:as:)
方法完成解码。
可以打断点
查看下topLevel、options、type、value
等信息
_JSONDecoder
fileprivate class _JSONDecoder : Decoder {
//本例中传入的container为topLevel,topLevel是一个字典,{"id": "101", "name": "GC"}
fileprivate init(referencing container: Any, at codingPath: [CodingKey] = [], options: JSONDecoder._Options) {
self.storage = _JSONDecodingStorage()
self.storage.push(container: container)
self.codingPath = codingPath
self.options = options
}
public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> {
guard !(self.storage.topContainer is NSNull) else { ... }
guard let topContainer = self.storage.topContainer as? [String : Any] else { ... }
let container = _JSONKeyedDecodingContainer<Key>(referencing: self, wrapping: topContainer)
return KeyedDecodingContainer(container)
}
......
}
fileprivate func unbox<T : Decodable>(_ value: Any, as type: T.Type) throws -> T? {
return try unbox_(value, as: type) as? T
}
fileprivate func unbox_(_ value: Any, as type: Decodable.Type) throws -> Any? {
if type == Date.self {
guard let date = try self.unbox(value, as: Date.self) else { return nil }
return date
} else if {
...
} else {
self.storage.push(container: value)
defer { self.storage.popContainer() }
//type为User, User.init(from: Decoder)
return try type.init(from: self)
}
}
在_JSONDecoder的init方法中有一个_JSONDecodingStorage类型的storage,负责存储container。
-
_JSONDecodingStorage
_JSONDecodingStorage中有一个属性containers,containers是一个数组,借助它实现栈来管理container。
接着看unbox
方法,
最后返回的表达式type.init(from: self)
相当于User.init(from: Decoder)
, 这不就是Decodable
协议要求实现的方法么,但是我们在示例中并没有实现这个方法,转为sil代码看一下
可以看到编译器自动帮我们生成了enum CodingKeys
和init(from:)
。
在init(from:)
方法中看可以看到通过调用Decoder的container
方法,后面的sil代码就不分析了,就是在做下面代码的事
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
}
在上面已经给出了container
方法的源码,在其中还有一个_JSONKeyedDecodingContainer。
-
_JSONKeyedDecodingContainer
对外用的是KeyedDecodingContainer
,而KeyedDecodingContainer
是通过_JSONKeyedDecodingContainer
进行初始化
。
fileprivate struct _JSONKeyedDecodingContainer<K : CodingKey> : KeyedDecodingContainerProtocol {
fileprivate init(referencing decoder: _JSONDecoder, wrapping container: [String : Any]) {
self.decoder = decoder
switch decoder.options.keyDecodingStrategy {
case .useDefaultKeys:
self.container = container
......
}
self.codingPath = decoder.codingPath
}
public var allKeys: [Key] {
return self.container.keys.compactMap { Key(stringValue: $0) }
}
public func decode(_ type: String.Type, forKey key: Key) throws -> String {
//通过key值从container中取出对应的值
guard let entry = self.container[key.stringValue] else {
throw ...
}
//把解码过的key记录在codingPath
self.decoder.codingPath.append(key)
defer { self.decoder.codingPath.removeLast() }
//把取出的值转为对应的类型
guard let value = try self.decoder.unbox(entry, as: String.self) else {
throw ...
}
return value
}
......
}
到这里整个decode
就完成了。
编码
编码相当于解码的逆过程,这里就简单看一下,与解码逻辑上差不多。
open class JSONEncoder {
open func encode<T : Encodable>(_ value: T) throws -> Data {
let encoder = _JSONEncoder(options: self.options)
guard let topLevel = try encoder.box_(value) else {
throw ...
}
let writingOptions = JSONSerialization.WritingOptions(rawValue: self.outputFormatting.rawValue).union(.fragmentsAllowed)
do {
return try JSONSerialization.data(withJSONObject: topLevel, options: writingOptions)
} catch {
throw ...
}
}
}
在_JSONEncoder的box_中调用Encodable要求实现的方法
fileprivate class _JSONEncoder : Encoder {
fileprivate func box_(_ value: Encodable) throws -> NSObject? {
......
do {
//调用Encodable的方法
try value.encode(to: self)
}
......
}
public func container<Key>(keyedBy: Key.Type) -> KeyedEncodingContainer<Key> {
let topContainer: NSMutableDictionary
if self.canEncodeNewValue {
topContainer = self.storage.pushKeyedContainer()
} else {
guard let container = self.storage.containers.last as? NSMutableDictionary else { ... }
topContainer = container
}
let container = _JSONKeyedEncodingContainer<Key>(referencing: self, codingPath: self.codingPath, wrapping: topContainer)
return KeyedEncodingContainer(container)
}
}
在encode()方法中调用container
方法拿到KeyedEncodingContainer
,然后再调用_JSONKeyedEncodingContainer
的encode
方法完成属性编码。
fileprivate struct _JSONKeyedEncodingContainer<K : CodingKey> : KeyedEncodingContainerProtocol {
public mutating func encode(_ value: String, forKey key: Key) throws {
self.container[_converted(key).stringValue._bridgeToObjectiveC()] = self.encoder.box(value)
}
}