我正在试验Decodable,无法弄清楚编译器如何为特定的Decodable
实例(如Book
)合成decode(_ type: Decodable.Protocol, forKey key: CodingKey)
函数。
struct Book: Codable {
var title: String
var pages: Int
var author: String
var coauthor: String
}
struct Bookstore: Codable {
var book: Book
var owner: String
}
我试着自己写一个decode(_:forKey:)
函数,这样我就可以更好地了解编译器是如何合成它的,但我完全空白。我开始为Book.Type
重载decode(_:forKey:)
函数,但这就是我所得到的。
extension KeyedDecodingContainer {
func decode(_ type: Book.Type, forKey key: CodingKey) throws -> Book {
print("I am about to decode a Book from the data keyed by (key.stringValue)")
// I have no clue what to put here
}
}
如果有人能帮我完成这个实现,我将非常感激,因为它将帮助我更好地理解解码过程。
提前感谢!
<标题>编辑:我已经试过了:
extension KeyedDecodingContainer {
func decode(_ type: Book.Type, forKey key: CodingKey) throws -> Book {
print("I am about to decode a Book from the data keyed by (key.stringValue)")
let decoder = try self.superDecoder(forKey: key as! K)
return try Book(from: decoder)
}
}
这是有效的,但我不知道as! K
到底在做什么,也不知道superDecoder(forKey:)
做什么。此外,我不确定在这种情况下使用强制铸造是否被认为是危险的。有更安全的方法吗?
我想你误解了一些东西,编译器不合成任何东西。decode
是泛型,其泛型参数T
约束为Decodable
。这意味着一旦声明了它,它将适用于所有符合Decodable
的类型。没有为Book
生成单独的实现。这都是相同的实现,只是不同的T
s。你应该关注的签名是:
func decode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer<K>.Key) throws -> T where T : Decodable
它是如何实现的?源代码说:
public func decode<T: Decodable>(
_ type: T.Type,
forKey key: Key
) throws -> T {
return try _box.decode(T.self, forKey: key)
}
所以像"如果你不得不这样做,你将如何实现[这个方法]?"不是很有意义,因为该方法已经被实现了,您不必自己实现它。
显然这个实现对于理解它是如何工作的不是很有用。它只是将调用委托给其他东西。调用最终被委托给KeyedDecodingContainerProtocol
,而KeyedDecodingContainerProtocol
的哪个实现取决于您正在使用的解码器。例如,JSONDecoder
使用的解码容器与PropertyListDecoder
不同。
现在的问题是,KeyedDecodingContainerProtocol
中的decode
方法是如何实现的?正如我们已经确定的,这取决于解码器。JSON解码器将做一些特定于JSON的事情,属性列表解码器将做一些特定于属性列表的事情,但最终它们可能会调用T.init(from:)
来获得T
的实例。KeyedDecodingContainerProtocol.decode
是您可以自己实现的。如果您正在编写自己的解码器,则需要实现此功能。T.init(from:)
也是你应该实现的东西,如果你想让你的可编码对象以自定义的方式解码。
我找不到JSONDecoder
的源代码,所以这里有一个开源的XML解码器,你可以探索:https://github.com/ShawnMoore/XMLParsing
这就是他们如何实现KeyedDecodingContainerProtocol.decode
的。这里调用unbox
,这里调用T.init(from:)
。