Десериализовать подкласс GKGraphNode с помощью NSKeyedUnarchiver

Я хочу сериализовать и десериализовать объект моего подкласса GKGraphNode, используя NSKeyedArchiver и NSKeyedUnarchiver. Итак, я пробую следующее:

//: 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)")
}

К сожалению, пример печатает только текст по умолчанию, а не ожидаемый тестовый текст:

Текст: Текст по умолчанию

Сначала я опустил удобный инициализатор. Но в этом случае он разбился с этой ошибкой:

ошибка: выполнение было прервано, причина: EXC_BAD_INSTRUCTION (код = EXC_I386_INVOP, субкод = 0x0). Процесс был оставлен в точке, где он был прерван, используйте thread return -x, чтобы вернуться в состояние до вычисления выражения.

GKGraphNodeSubclass.playground: 5: 7: Неустранимая ошибка: использование нереализованного инициализатора 'init()' для класса '__lldb_expr_58.MyGraphNode'

Кто-нибудь может объяснить, почему тестовый текст игнорируется во время десериализации?
Или почему я вообще должен добавлять удобный инициализатор?


person Ruben    schedule 18.03.2018    source источник


Ответы (2)


Мне помогли на форуме разработчиков Apple:

Свойство text в конечном итоге сбрасывается с помощью super.init(coder: aDecoder), предположительно потому, что это вызывает init() внутри, и это заканчивается по вашему усмотрению init(). Это было бы незаконно в Swift, но допустимо в Obj-C, в котором нет таких же строгих правил инициализации.

Решение состоит в том, чтобы инициализировать текст после super.init(coder:), а не до него. Это означает, что вы не можете использовать свойство let

Чтобы исправить мой пример, я изменил объявление переменной и инициализатор NSCoding следующим образом:

var text: String!

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

    text = aDecoder.decodeObject(forKey: MyGraphNode.textCodingKey) as! String
}
person Ruben    schedule 19.03.2018

Согласен с Рубеном, с небольшим дополнением

Также может потребоваться переопределить init(), чтобы убедиться, что он существует. Из-за строгих правил инициализации Swift

override init() {
    super.init()
}
person Petzep    schedule 14.11.2019