在继承中使用Swift 4中的Decodable
应该使用类继承来破坏类的可解析性。例如,下面的代码在继承中使用Swift 4中的Decodable
class Server : Codable {
var id : Int?
}
class Development : Server {
var name : String?
var userId : Int?
}
var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development
print(item.id ?? "id is nil")
print(item.name ?? "name is nil") here
输出为:
1
name is nil
现在,如果我扭转这种,名解码但ID不会。
class Server {
var id : Int?
}
class Development : Server, Codable {
var name : String?
var userId : Int?
}
var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development
print(item.id ?? "id is nil")
print(item.name ?? "name is nil")
输出为:
id is nil
Large Building Development
而且你不能在这两个类表达可编码。
我相信在继承的情况下,你必须自己实现Coding
。也就是说,您必须在超类和子类中指定CodingKeys
并实施init(from:)
和encode(to:)
。根据WWDC video(大约49:28,如下图),您必须使用超级编码器/解码器调用super。
required init(from decoder: Decoder) throws {
// Get our container for this subclass' coding keys
let container = try decoder.container(keyedBy: CodingKeys.self)
myVar = try container.decode(MyType.self, forKey: .myVar)
// otherVar = ...
// Get superDecoder for superclass and call super.init(from:) with it
let superDecoder = try container.superDecoder()
try super.init(from: superDecoder)
}
视频似乎停止短显示编码端的(但它是container.superEncoder()
为encode(to:)
侧),但它在几乎相同的方式在encode(to:)
实现。我可以在这个简单的例子中证实这一点(参见下面的游乐场代码)。
我仍然有一些奇怪的行为,自己疲于应付更为复杂的模型,我从NSCoding
转换,它有很多新的嵌套类型(包括struct
和enum
)的多数民众赞成出现此意外nil
行为,“不应该不会“。请注意,可能存在涉及嵌套类型的边缘案例。
编辑:嵌套类型似乎在我的测试操场上正常工作;我现在怀疑自引用类(认为树节点的孩子)有自己的集合,其中也包含该类的各个子类的实例。一个简单的自引用类的测试可以很好地解码(也就是说,没有子类),所以我现在正在关注为什么子类失败的原因。
更新6月25日'17:我最终提交一个bug与苹果关于这一点。不幸的是,包含Subclass: Superclass
元素的Superclass
数组的编码/解码周期将导致阵列中的所有元素被解码为Superclass
(子类'0从不被调用,导致数据丢失或更糟糕)。
//: Fully-Implemented Inheritance
class FullSuper: Codable {
var id: UUID?
init() {}
private enum CodingKeys: String, CodingKey { case id }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
}
}
class FullSub: FullSuper {
var string: String?
private enum CodingKeys: String, CodingKey { case string }
override init() { super.init() }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let superdecoder = try container.superDecoder()
try super.init(from: superdecoder)
string = try container.decode(String.self, forKey: .string)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(string, forKey: .string)
let superdecoder = container.superEncoder()
try super.encode(to: superdecoder)
}
}
let fullSub = FullSub()
fullSub.id = UUID()
fullSub.string = "FullSub"
let fullEncoder = PropertyListEncoder()
let fullData = try fullEncoder.encode(fullSub)
let fullDecoder = PropertyListDecoder()
let fullSubDecoded: FullSub = try simpleDecoder.decode(FullSub.self, from: fullData)
在fullSubDecoded
中恢复了超类和子类的属性。
很高兴我现在至少可以在这个问题上保持和平...... –
现在可以通过将基类转换为协议并向协议扩展添加默认实现并且派生类符合它 –
同查尔顿。使用基类解码时遇到EXC_BAD_ACCESS错误。必须转到协议结构才能解决它。 –
我能够通过使我的基类和子类符合Decodable
而不是Codable
来使其工作。如果我使用了Codable
,它会以奇怪的方式崩溃,例如在访问子类的字段时获取EXC_BAD_ACCESS
,但调试器可以毫无问题地显示所有的子类值。
此外,将superDecoder传递给基类super.init()
不起作用。我只是将解码器从子类传递给基类。
同样的技巧:将superDecoder传递给super.init()中的基类不起作用。我只是将解码器从子类传递给基类。 –
如何使用,而不是经典的继承组成,使东西更简单
protocol Parent: Codable {
var inheritedProp: Int? {get set}
}
struct Child: Parent {
var inheritedProp: Int?
var title: String?
enum CodingKeys: String, CodingKey {
case inheritedProp = "inherited_prop"
case title = "short_title"
}
}
上组成附加信息:http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/
这是如何解决解码异构阵列的问题的? –
只是要清楚,这不是严厉的批评。我一直在重新讨论存储异构集合的问题,但无济于事。一个通用的解决方案是最好的,这意味着我们无法知道解码时的类型。 –
在“帮助”>“开发人员文档”下的Xcode中,搜索名为“编码和解码自定义类型”的精彩文章。我认为阅读会帮助你。 –
Found This Link - Go down to inheritance section
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(employeeID, forKey: .employeeID)
}
进行解码我这样做:
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
let values = try decoder.container(keyedBy: CodingKeys.self)
total = try values.decode(Int.self, forKey: .total)
}
private enum CodingKeys: String, CodingKey
{
case total
}
有趣。你提交了苹果公司的错误吗? –
这不是一个错误,它实际上是一个“无证的功能”。 :-)该解决方案中唯一提及(一半)的解决方案是在2017年WWDC“基金会新增内容”视频中,详见我的答案。 –