Swift 4 可编码领域对象子类

Posted

技术标签:

【中文标题】Swift 4 可编码领域对象子类【英文标题】:Swift 4 Codable Realm Object Subclasses 【发布时间】:2017-11-02 16:59:49 【问题描述】:

试图将我的一些代码库切换到 Swift 4 的新漂亮 Codable 协议。我的设置如下所示:

class Base: Object, Codable 

    dynamic var id: String = ""
    dynamic var timestamp: String = ""

    private enum CodingKeys: String, CodingKey 

        case id = "_id"
        case timestamp = "timestamp"

    



class User: Base 

    dynamic var name: String = ""

    private enum CodingKeys: String, CodingKey 
        case name = "name"
    

    required init(from decoder: Decoder) throws 

        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
        try super.init(from: decoder)

    


我有一个符合Codable 的基本领域对象类,以及一个Base 的子类,它也有自己的编码键。我在User 子类上覆盖init(decoder:) 以映射我的其他编码键,然后调用super.init(decoder:) 来映射Base 的编码键。但是,一旦我覆盖了init(decoder:),我就会收到以下错误:

必需的初始化程序“init()”必须由“Base”的子类提供 必需的初始化程序“init(realm:schema:)”必须由“Base”的子类提供 必需的初始化程序“init(value:schema:)”必须由“Base”的子类提供

我不确定解决这些问题的正确方法是什么。

【问题讨论】:

不支持RealmObjects之间的继承 看起来应该支持对象之间的继承:realm.io/docs/swift/latest/#model-inheritance 【参考方案1】:

您不能覆盖 init() 或 Realm Object 的其他初始化程序。您可以改用便捷初始化程序。那么你不能调用super.init(from:),所以定义一个解码超类属性的方法,比如Base.decode(from:)

参见以下代码示例:

class Base: Object, Codable 
    dynamic var id: String = ""
    dynamic var timestamp: String = ""

    private enum CodingKeys: String, CodingKey 
        case id = "_id"
        case timestamp = "timestamp"
    

    func decode(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.timestamp = try container.decode(String.self, forKey: .timestamp)
    


class User: Base 
    dynamic var name: String = ""

    private enum CodingKeys: String, CodingKey 
        case id = "_id"
        case timestamp
        case name = "name"
    

    required convenience init(from decoder: Decoder) throws 
        self.init()

        try decode(from: decoder)

        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.name = try container.decode(String.self, forKey: .name)
    

【讨论】:

【参考方案2】:

你不能只覆盖一个类的初始化器。如果您要覆盖,请为所有这些覆盖。如果您并不真正使用或关心其他初始化程序,至少只需在其中调用 super.<respective init>。 对于init(realm: RLMRealm, schema: RLMObjectSchema)init(value: Any, schema: RLMSchema),编译器会抱怨它不知道RLMRealm、RLMObjectSchema 和RLMSchema 是什么。所以除了 RealmSwift 之外还要导入 Realm。

【讨论】:

【参考方案3】:

正如我刚刚在 another question, 上回答的那样,虽然您可以按照上面 Katsumi 的建议将您的子类用作 Codable 类型,但您可能会遇到另一个问题。

您不能将 Base 的集合作为包含子类实例的引用类型并使该集合在 Codable 中存在。它只会解码为Base 实例。

多态持久性似乎被设计破坏了

错误报告SR-5331 引用了他们在Radar. 上得到的回复

与现有的 NSCoding API (NSKeyedArchiver) 不同,为了灵活性和安全性,新的 Swift 4 Codable 实现不会将有关编码类型的类型信息写入生成的档案中。因此,在解码时,API 只能使用您提供的具体类型来解码值(在您的情况下是超类类型)。

这是设计使然——如果您需要执行此操作所需的动力,我们建议您采用 NSSecureCoding 并使用 NSKeyedArchiver/NSKeyedUnarchiver

我不为所动,从所有精彩的文章中想到 Codable 是我的一些祈祷的答案。作为对象工厂的一组并行 Codable 结构是我正在考虑的一种解决方法,以保留类型信息。

【讨论】:

以上是关于Swift 4 可编码领域对象子类的主要内容,如果未能解决你的问题,请参考以下文章

Swift Firebase - 将估计的 ServerTimestamp 与可编码的自定义对象相结合

Swift 4 中默认情况下的可编码枚举

在 swift 4 中使用可编码的 JSON 时出错?

单个 JSON 返回中的多个可编码对象类型

在swift 4中调用Encoding类型的网络

将对象转换为可编码对象失败