使用 NSKeyedUnarchiver 反序列化 GKGraphNode 的子类

Deserialize subclass of GKGraphNode using NSKeyedUnarchiver

我想使用 NSKeyedArchiver 和 NSKeyedUnarchiver 序列化和反序列化我的 GKGraphNode 子类的对象。所以我尝试以下操作:

//: Playground - noun: a place where people can play

import GameplayKit

class MyGraphNode: GKGraphNode {
    static let textCodingKey = "TextCodingKey"

    let text: String

    override convenience init() {
        self.init(text: "Default Text")
    }

    init(text: String) {
        self.text = text

        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        text = aDecoder.decodeObject(forKey: MyGraphNode.textCodingKey) as! String

        super.init(coder: aDecoder)
    }

    override func encode(with aCoder: NSCoder) {
        super.encode(with: aCoder)

        aCoder.encode(text, forKey: MyGraphNode.textCodingKey)
    }
}

let text = "Test Text"

let graphNode = MyGraphNode(text: text)

let data = NSKeyedArchiver.archivedData(withRootObject: graphNode)

if let unarchivedGraphNode = NSKeyedUnarchiver.unarchiveObject(with: data) as? MyGraphNode {
    print("Text: \(unarchivedGraphNode.text)")
}

不幸的是,该示例仅打印默认文本而不是预期的测试文本:

Text: Default Text

首先我省略了便利初始化器。但在这种情况下,它因以下错误而崩溃:

error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.

GKGraphNodeSubclass.playground: 5: 7: Fatal error: Use of unimplemented initializer 'init()' for class '__lldb_expr_58.MyGraphNode'

谁能解释为什么在反序列化过程中忽略了测试文本?
或者为什么我必须添加便利初始化程序?

我在 Apple Developer Forum 中得到了帮助:

The "text" property is ending up being reset by "super.init(coder: aDecoder)", presumably because that calls "init()" internally, and that ends up at your convenience "init ()". This would be illegal in Swift, but it's legal in Obj-C, which doesn't have the same strict initialization rules.

The solution is to initialize "text" after the super.init(coder:), rather than before. This means you can't use a "let" property

为了修复我的示例,我更改了变量声明和 NSCoding 初始值设定项,如下所示:

var text: String!

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)

    text = aDecoder.decodeObject(forKey: MyGraphNode.textCodingKey) as! String
}

我同意 Ruben 的观点,但有一点补充

可能还需要覆盖 init() 以确保它存在。由于Swift

严格的初始化规则
override init() {
    super.init()
}