2017-06-14 132 views
23

应该使用类继承来破坏类的可解析性。例如,下面的代码在继承中使用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 

而且你不能在这两个类表达可编码。

+1

有趣。你提交了苹果公司的错误吗? –

+0

这不是一个错误,它实际上是一个“无证的功能”。 :-)该解决方案中唯一提及(一半)的解决方案是在2017年WWDC“基金会新增内容”视频中,详见我的答案。 –

回答

28

我相信在继承的情况下,你必须自己实现Coding。也就是说,您必须在超类和子类中指定CodingKeys并实施init(from:)encode(to:)。根据WWDC video(大约49:28,如下图),您必须使用超级编码器/解码器调用super。

WWDC 2017 Session 212 Screenshot at 49:28 (Source Code)

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转换,它有很多新的嵌套类型(包括structenum)的多数民众赞成出现此意外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中恢复了超类和子类的属性。

+0

很高兴我现在至少可以在这个问题上保持和平...... –

+3

现在可以通过将基类转换为协议并向协议扩展添加默认实现并且派生类符合它 –

+0

同查尔顿。使用基类解码时遇到EXC_BAD_ACCESS错误。必须转到协议结构才能解决它。 –

3

我能够通过使我的基类和子类符合Decodable而不是Codable来使其工作。如果我使用了Codable,它会以奇怪的方式崩溃,例如在访问子类的字段时获取EXC_BAD_ACCESS,但调试器可以毫无问题地显示所有的子类值。

此外,将superDecoder传递给基类super.init()不起作用。我只是将解码器从子类传递给基类。

+0

同样的技巧:将superDecoder传递给super.init()中的基类不起作用。我只是将解码器从子类传递给基类。 –

3

如何使用,而不是经典的继承组成,使东西更简单

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/

+2

这是如何解决解码异构阵列的问题的? –

+1

只是要清楚,这不是严厉的批评。我一直在重新讨论存储异构集合的问题,但无济于事。一个通用的解决方案是最好的,这意味着我们无法知道解码时的类型。 –

+0

在“帮助”>“开发人员文档”下的Xcode中,搜索名为“编码和解码自定义类型”的精彩文章。我认为阅读会帮助你。 –

0

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 

}