如何使用 NSCoding 将新键和值添加到现有键控存档

How to add new key and value to existing keyed archives using NSCoding

正在尝试添加新密钥 "password"。该应用程序的现有版本具有这些现有键 "name"、"ip"、"port",并且具有存储值。添加新密钥 "password" 时,应用程序无法读取现有数据,并显示此错误 "Unable to decode the name for a System object." 来自以下代码:

guard let password = aDecoder.decodeObject(forKey: PropertyKey.password) as? String else {
            os_log("Unable to decode the name for a System object.", log: OSLog.default, type: .debug)
            return nil
        }

我的问题是如何添加新密钥 "password" 将默认值“123456”分配给所有现有的密钥存档 (data/objects),而不破坏现有数据。

e.g.
name = "System01", ip = "10.0.0.3", port = "3535", password = "123456" 
name = "System02", ip = "10.0.0.4", port = "3535", password = "123456" 

==============================

import UIKit
import os.log

class System: NSObject, NSCoding {


    //MARK: Properties

    var name: String
    var ip: String
    var port: String
    var password: String. // Added this new key

    //MARK: Archiving Paths

    static let DocumentsDirectory = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
    static let ArchiveURL = DocumentsDirectory.appendingPathComponent("systems")

    //MARK: Types

    struct PropertyKey {
        static let name = "name"
        static let ip = "ip"
        static let port = "port"
        static let password = "password"  // Added this new key
    }

    //MARK: Initialization

    init(name: String, ip: String, port: String, password: String) {

        // Initialize stored properties.
        self.name = name
        self.ip = ip
        self.port = port
        self.password = password  // Added this new key
    }

    //MARK: NSCoding

    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: PropertyKey.name)
        aCoder.encode(ip, forKey: PropertyKey.ip)
        aCoder.encode(port, forKey: PropertyKey.port)
        aCoder.encode(password, forKey: PropertyKey.password)  // Added this new key
    }

    required convenience init?(coder aDecoder: NSCoder) {

        // The name is required. If we cannot decode a name string, the initializer should fail.
        guard let name = aDecoder.decodeObject(forKey: PropertyKey.name) as? String else {
            os_log("Unable to decode the name for a System object.", log: OSLog.default, type: .debug)
            return nil
        }
        guard let ip = aDecoder.decodeObject(forKey: PropertyKey.ip) as? String else {
            os_log("Unable to decode the name for a System object.", log: OSLog.default, type: .debug)
            return nil
        }
        guard let port = aDecoder.decodeObject(forKey: PropertyKey.port) as? String else {
            os_log("Unable to decode the name for a System object.", log: OSLog.default, type: .debug)
            return nil
        }
        guard let password = aDecoder.decodeObject(forKey: PropertyKey.password) as? String else {
            os_log("Unable to decode the name for a System object.", log: OSLog.default, type: .debug)
            return nil
        }  // Added this new key

        // Must call designated initializer.
        self.init(name: name, ip: ip, port: port, password: password)

    }
}

在没有密码的情况下,您的代码不能失败。用这样的东西替换 guard let password ... 部分:

let decodedPassword = aDecoder.decodeObject(forKey: PropertyKey.password) as? String
let password = decodedPassword ?? "123456"

然后

self.init(name: name, ip: ip, port: port, password: password)

如果您想在不 崩溃 的情况下支持当前应用程序,您必须创建一个继承自 class "System" 的新 class。此外,您还必须通过使用适当的检查来适当地编码和解码对象来处理当前场景和未来场景。

当然,您可以按照@Gereon 提到的代码进行操作,但是如果您想添加更多键值,最好扩展 class.