iOS中unarchiveObject(with:)已弃用,[String: Any?]的替代方案?
针对[String: Any?]类型的NSKeyedUnarchiver替代方案
因为Swift的[String: Any?]无法直接作为unarchivedObjectOfClass:fromData:error:的参数(Any?不遵循NSCoding,且泛型字典类型无法直接匹配Objective-C类),可以通过以下两种实用方案解决:
方案一:借助NSDictionary中转(推荐轻量场景)
利用NSDictionary自动将Swift的nil转为NSNull的特性,先把目标字典转成Objective-C兼容的容器,再进行归档/解档,最后转回Swift字典并处理NSNull为nil。
归档代码
func archiveDictionary(_ dict: [String: Any?]) throws -> Data { // 将Swift字典转为NSDictionary,nil自动转为NSNull let nsDict = dict as NSDictionary return try NSKeyedArchiver.archivedData(withRootObject: nsDict, requiringSecureCoding: false) } // 存入UserDefaults示例 let req = ["a":nil, "b":3, "c":["d":3.3, "e":"e", "f":[]], "g":[]] if let data = try? archiveDictionary(req) { UserDefaults.standard.set(data, forKey: "SavedDict") }
解档代码
需要递归处理嵌套结构,把NSNull转回nil,同时处理嵌套的字典和数组:
func convertNSContainerToSwift(_ value: Any) -> Any? { switch value { case let null as NSNull: return nil case let nsDict as NSDictionary: var swiftDict = [String: Any?]() for (key, value) in nsDict { if let keyStr = key as? String { swiftDict[keyStr] = convertNSContainerToSwift(value) } } return swiftDict case let nsArray as NSArray: return nsArray.map { convertNSContainerToSwift($0) } default: return value } } func unarchiveDictionary(from data: Data) throws -> [String: Any?] { guard let nsDict = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSDictionary.self, from: data) else { throw NSError(domain: "UnarchiveError", code: 0, userInfo: [NSLocalizedDescriptionKey: "解档失败"]) } return convertNSContainerToSwift(nsDict) as? [String: Any?] ?? [:] } // 从UserDefaults读取示例 if let data = UserDefaults.standard.data(forKey: "SavedDict"), let dict = try? unarchiveDictionary(from: data) { print(dict) // 输出原始结构,nil保留 }
方案二:自定义NSCoding包装类(适合复杂场景)
创建一个遵循NSCoding的包装类,专门处理[String: Any?]的编码和解码,手动处理每个键值对的嵌套结构:
class AnyDictionaryWrapper: NSObject, NSCoding { let dict: [String: Any?] init(dict: [String: Any?]) { self.dict = dict } required init?(coder: NSCoder) { var tempDict = [String: Any?]() // 解码所有键 guard let keys = coder.decodeObject(forKey: "keys") as? [String] else { return nil } for key in keys { let value = coder.decodeObject(forKey: key) // 处理嵌套的包装类或数组 if let wrapper = value as? AnyDictionaryWrapper { tempDict[key] = wrapper.dict } else if let array = value as? [Any] { tempDict[key] = array.map { if let itemWrapper = $0 as? AnyDictionaryWrapper { return itemWrapper.dict } else if $0 is NSNull { return nil } else { return $0 } } } else if value is NSNull { tempDict[key] = nil } else { tempDict[key] = value } } self.dict = tempDict } func encode(with coder: NSCoder) { let keys = Array(dict.keys) coder.encode(keys, forKey: "keys") for (key, value) in dict { switch value { case let subDict as [String: Any?]: coder.encode(AnyDictionaryWrapper(dict: subDict), forKey: key) case let array as [Any?]: let encodedArray = array.map { item in if let subDict = item as? [String: Any?] { return AnyDictionaryWrapper(dict: subDict) } else { return item ?? NSNull() } } coder.encode(encodedArray, forKey: key) case nil: coder.encode(NSNull(), forKey: key) default: coder.encode(value, forKey: key) } } } }
归档和解档用法
// 归档 let wrapper = AnyDictionaryWrapper(dict: req) if let data = try? NSKeyedArchiver.archivedData(withRootObject: wrapper, requiringSecureCoding: false) { UserDefaults.standard.set(data, forKey: "SavedDict") } // 解档 if let data = UserDefaults.standard.data(forKey: "SavedDict"), let wrapper = try? NSKeyedUnarchiver.unarchivedObject(ofClass: AnyDictionaryWrapper.self, from: data) { let dict = wrapper.dict print(dict) }
注意:如果你的字典中包含自定义类型,需要确保这些类型也遵循
NSCoding或Codable,并在转换逻辑中额外处理。
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

