You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中如何通过NSCoding正确编解码自定义对象字典?

正确的NSCoding编解码方案

首先明确核心问题:你直接编码字典崩溃,是因为ItemType和ItemEntity没有遵守NSCoding协议;后续解码崩溃,是因为编码时把字典转成了Data存储,但解码时直接尝试把Data强转成字典,类型不匹配导致nil。

步骤1:确保自定义类型遵守NSCoding

1.1 让ItemType(枚举)实现NSCoding

如果ItemType是枚举,需要实现NSCoding协议来支持编解码:

enum ItemType: String, NSCoding {
    case weapon, armor, accessory
    
    func encode(with coder: NSCoder) {
        coder.encode(rawValue, forKey: "rawValue")
    }
    
    required init?(coder: NSCoder) {
        guard let rawValue = coder.decodeObject(forKey: "rawValue") as? String else {
            return nil
        }
        self.init(rawValue: rawValue)
    }
}

1.2 让ItemEntity实现NSCoding

ItemEntity作为自定义类,必须继承NSObject并遵守NSCoding:

class ItemEntity: NSObject, NSCoding {
    var name: String
    var level: Int
    
    init(name: String, level: Int) {
        self.name = name
        self.level = level
    }
    
    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
        coder.encode(level, forKey: "level")
    }
    
    required init?(coder: NSCoder) {
        self.name = coder.decodeObject(forKey: "name") as! String
        self.level = coder.decodeInteger(forKey: "level")
    }
}

步骤2:实现包含equippedItems的类的编解码

方案1:直接编码字典(推荐)

只要ItemType和ItemEntity都遵守NSCoding,就可以直接编码字典,无需转成Data:

class YourClass: NSObject, NSCoding {
    var equippedItems = [ItemType: ItemEntity]()
    
    override init() {}
    
    override func encode(with aCoder: NSCoder) {
        aCoder.encode(equippedItems, forKey: "Equiptments")
    }
    
    required convenience init?(coder aDecoder: NSCoder) {
        self.init()
        // 用可选绑定+默认值替代强制解包,避免崩溃
        equippedItems = aDecoder.decodeObject(forKey: "Equiptments") as? [ItemType: ItemEntity] ?? [:]
    }
}

方案2:转成Data编码(兼容你的原有编码方式)

如果坚持用NSKeyedArchiver转Data的方式,解码时要先解析Data再转成字典:

class YourClass: NSObject, NSCoding {
    var equippedItems = [ItemType: ItemEntity]()
    
    override init() {}
    
    override func encode(with aCoder: NSCoder) {
        do {
            let data = try NSKeyedArchiver.archivedData(withRootObject: equippedItems, requiringSecureCoding: false)
            aCoder.encode(data, forKey: "Equiptments")
        } catch {
            print("编码失败:\(error)")
        }
    }
    
    required convenience init?(coder aDecoder: NSCoder) {
        self.init()
        guard let data = aDecoder.decodeObject(forKey: "Equiptments") as? Data else {
            equippedItems = [:]
            return
        }
        do {
            equippedItems = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [ItemType: ItemEntity] ?? [:]
        } catch {
            print("解码失败:\(error)")
            equippedItems = [:]
        }
    }
}

关键注意事项

  • 永远避免用as!强制解包,改用as?配合默认值,防止nil导致崩溃。
  • 所有参与编解码的自定义类型(包括字典的Key和Value)都必须遵守NSCoding协议,否则直接编码字典会崩溃。

内容的提问来源于stack exchange,提问作者Bob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 18:13:30