macOS14.0后NSKeyedUnarchiver安全解码Swift数组为nil问题
问题
自macOS 14.0起,NSKeyedUnarchiver.unarchiveTopLevelObjectWithData已被废弃。在类中启用NSSecureCoding后,使用coder.unarchivedObject(ofClasses classes: [AnyClass], from data: Data)加载旧NSCoder归档时,遇到Swift数组解码异常:
- 归档可正常加载,多数类成员能提取,但Swift数组始终返回nil,尽管
coder.containsValue(forKey:)返回true - 数组原本的编码代码:
var pageUsesClearBackground: Array<Bool> = [true, true, true, true] coder.encode(pageUsesClearBackground, forKey: "CB_KEY")
- 使用
pageUsesClearBackground = coder.decodeObject(forKey: "CB_KEY") as? Array<Bool>解码时,数组元素数量为0;但使用非安全解码版本时数组能正常填充 - 曾怀疑问题和Swift数组是结构体、不遵循NSCoding有关,但此前多年编码解码都正常工作,希望找到从NSSecureCoding出现前的归档中恢复数组类型类成员的方法
解决方法
问题的核心是必须将类依赖的所有关联类都添加到安全解码的允许类集合中。
在测试应用中,控制台输出了关键警告:
NSCoderTest[11222:509325] [general] *** -[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber' (0x2140200a8) [/System/Library/Frameworks/Foundation.framework]' for key 'NS.objects', even though it was not explicitly included in the client allowed classes set: '{ "'NSCoderTest.Test' (0x100eb0700) , "'NSArray' (0x213ff2440) [/System/Library/Frameworks/CoreFoundation.framework]" )}'. This will be disallowed in the future.
原因是Swift的Array<Bool>在归档时会被桥接为NSArray,而数组中的Bool值会被转换为NSNumber存储。启用NSSecureCoding后,安全解码会验证所有层级的类型:不仅要把NSArray加入允许类列表,还要把数组元素对应的NSNumber也加入,否则会因元素类型验证失败,导致数组解码为空或nil。
调整允许类集合,加入NSNumber后即可正常解码数组,示例代码:
let allowedClasses: [AnyClass] = [Test.self, NSArray.self, NSNumber.self] guard let decodedObject = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: allowedClasses, from: data) as? Test else { // 处理解码失败逻辑 return }
注:该警告在Xcode14.1中未出现,但后续系统版本会强制要求显式声明所有依赖类。
内容的提问来源于stack exchange,提问作者Chris Walken
相关产品推荐
相关产品推荐

