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
相关产品推荐
相关产品推荐

