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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:55:16