当我不知道类型时如何使用 class 继承进行解码?

How can I decode when I don't know the type, with class inheritance?

我有一个基数 class Action,它是一个 Operation。它里面有一堆笨拙的 Operation 东西(KVO 等等)。基础 class 本身实际上不需要 encode/decode 任何东西。

class Action : Operation, Codable {
    var _executing = false
    ...
}

我有一堆 Action sub-classes,比如 DropboxUploadAction,它们直接用它们定义的 Input 结构实例化:

let actionInput = DropboxUploadAction.Input.init(...)
ActionManager.shared.run(DropboxUploadAction.init(actionInput, data: binaryData), completionBlock: nil)

下面是子class的样子:

class DropboxUploadAction : Action {
    struct Input : Codable {
        var guid: String
        var eventName: String
        var fileURL: URL?
        var filenameOnDropbox: String
        var share: Bool
    }

    struct Output : Codable {
        var sharedFileLink: String?
        var dropboxPath: String?
    }

    var input: Input
    var output: Output

    ...

    required init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        input = try values.decode(Input.self, forKey: .input)
        output = try values.decode(Output.self, forKey: .output)
        let superDecoder = try values.superDecoder()
        try super.init(from: superDecoder)
    }

    fileprivate enum CodingKeys: String, CodingKey {
        case input
        case output
    }

    override func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(input, forKey: .input)
        try container.encode(output, forKey: .output)
        try super.encode(to: container.superEncoder())
    }
}

当某些情况发生时,例如互联网连接丢失,这些 classes 需要序列化到磁盘以备后用。没关系,因为当时我有对它们的引用并且可以用 JSONEncoder().encode(action) 对它们进行编码,没问题。

但是后来我想反序列化它们时,我需要指定class的类型,但我不知道它是什么。我有一些数据,我知道它可以解码为继承自 Action 的 class,但我不知道它是哪个子 class。我讨厌在文件名中对其进行编码。有没有办法将它解码为基础 class Action,然后在 Actiondecode() 方法中,以某种方式检测正确的 class 并重定向?

过去我用 NSKeyedUnarchiver.setClass() 来处理这个问题。但我不知道如何用 Swift 4 的 Codable 做到这一点,我知道 NSCoding 现在已被弃用所以我不应该再使用 NSKeyedUnarchiver...

如果有帮助:我有一个 struct Types : OptionSet, Codable,每个子 class returns,所以我不必使用 class 的名称作为它的身份。

感谢您的帮助!

NSCoding 没有弃用。我们仍然在通过 init(coder:) 从故事板实例化 UIViewController 时使用它。

此外,如果您仍然不想使用 NSCoding,您可以将 InputOutputTypes 存储到结构中并序列化它改为磁盘。

struct SerializedAction {
  let input: Input
  let output: Output
  let type: Type
}

需要时,您可以对其进行解码并确定正确的 Action 以通过 type 属性 使用您的 input/output 进行初始化。

class DropboxAction: Action {
  ...
  init(input: Input, output: Output) {
  ...
  }
}

您不一定需要对整个 Action 对象进行编码。