如何使用NSCoding编码解码Hashable类型的Swift枚举?
为带关联值的Hashable枚举实现NSCoding
你的问题核心在于ItemCatalog是带关联值的枚举,它本身未实现NSCoding,直接编码枚举实例会崩溃;而用hashValue编码完全不可行——哈希值不具备唯一性,也无法反向还原枚举的具体case和关联值。
正确的做法是拆分枚举的case类型和关联值分别编码,解码时再根据存储的信息重构枚举实例。以下是完整的实现示例:
假设你持有ItemCatalog的类是Item(如果是其他类,只需对应修改属性名):
class Item: NSObject, NSCoding { let catalogCode: ItemCatalog init(itemCatalogCode: ItemCatalog) { self.catalogCode = itemCatalogCode super.init() } // MARK: - NSCoding 编码实现 func encode(with aCoder: NSCoder) { switch catalogCode { case .sword(let sword): // 编码case类型标识和关联值的rawValue aCoder.encode("sword", forKey: "caseType") aCoder.encode(sword.rawValue, forKey: "associatedValue") case .shield(let shield): aCoder.encode("shield", forKey: "caseType") aCoder.encode(shield.rawValue, forKey: "associatedValue") case .armor(let armor): aCoder.encode("armor", forKey: "caseType") aCoder.encode(armor.rawValue, forKey: "associatedValue") } } // MARK: - NSCoding 解码实现 required init?(coder aDecoder: NSCoder) { // 读取存储的case类型和关联值 guard let caseType = aDecoder.decodeObject(forKey: "caseType") as? String, let rawValue = aDecoder.decodeObject(forKey: "associatedValue") as? String else { return nil } // 根据case类型重构枚举实例 switch caseType { case "sword": guard let sword = Swords(rawValue: rawValue) else { return nil } catalogCode = .sword(sword) case "shield": guard let shield = Shields(rawValue: rawValue) else { return nil } catalogCode = .shield(shield) case "armor": guard let armor = Armors(rawValue: rawValue) else { return nil } catalogCode = .armor(armor) default: return nil } super.init() } }
关键说明
编码逻辑:
- 用字符串(如
sword/shield/armor)标记当前枚举的case类型,确保解码时能区分不同分支 - 利用子枚举的
rawValue(因为它们都是String类型枚举)存储关联值,String是NSCoder原生支持的可编码类型
- 用字符串(如
解码逻辑:
- 先读取case类型和关联值的rawValue,做安全类型转换避免崩溃
- 根据case类型尝试将rawValue转换为对应的子枚举,校验失败则返回nil(符合
init?(coder:)的可失败要求) - 最后用验证后的子枚举重构
ItemCatalog实例
为什么之前的方法不行
- 直接编码
catalogCode:ItemCatalog不是NSObject也未实现NSCoding,NSCoder无法直接处理自定义枚举类型 - 编码
hashValue:哈希值仅用于哈希表快速查找,不保证唯一,且无法反向推导枚举的具体case和关联值,完全无法完成可靠的解码
内容的提问来源于stack exchange,提问作者Bob
相关产品推荐
相关产品推荐

