已添加NSString等类仍触发NSKeyedUnarchiver未显式包含类警告
我用以下代码从UserDefaults解归档自定义对象数组:
guard let data = UserDefaults.standard.data(forKey: "key") else { return } let customClassArray = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSMutableArray.self, NSNull.self, NSMutableDictionary.self, NSNumber.self, NSString.self, NSArray.self, CustomClass.self], from: data) as? [CustomClass]
但反复收到这类警告:
[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSString' (0x1e5fae918) [/System/Library/Frameworks/Foundation.framework]' for key 'NS.keys', even though it was not explicitly included in the client allowed classes set: '{( "'NSMutableArray' (0x1e5fa7050) [/System/Library/Frameworks/CoreFoundation.framework]", "'NSNull' (0x1e5fa68a8) [/System/Library/Frameworks/CoreFoundation.framework]", "'NSMutableDictionary' (0x1e5fa6fb0) [/System/Library/Frameworks/CoreFoundation.framework]" )}'. This will be disallowed in the future.
以及:
[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber' (0x1e5fae8c8) [/System/Library/Frameworks/Foundation.framework]' for key 'NS.objects', even though it was not explicitly included in the client allowed classes set: '{( "'NSMutableArray' (0x1e5fa7050) [/System/Library/Frameworks/CoreFoundation.framework]", "'NSNull' (0x1e5fa68a8) [/System/Library/Frameworks/CoreFoundation.framework]", "'NSMutableDictionary' (0x1e5fa6fb0) [/System/Library/Frameworks/CoreFoundation.framework]"
明明我已经在allowedClasses里列出了NSString、NSNumber这些类,为什么还是会触发警告?
问题原因与解决方法
核心原因
警告里显示的允许类集合仅包含NSMutableArray、NSNull、NSMutableDictionary,说明你传入的完整类列表未被正确识别。大概率是以下原因导致:
- 调用
unarchivedObject(ofClasses:from:)时,同时传入可变和不可变容器类(比如NSMutableArray和NSArray),导致NSKeyedUnarchiver在验证时优先匹配可变容器,但未正确关联容器内元素的允许类。 - 历史归档数据是用旧逻辑生成的,解归档时的类列表无法被容器类正确继承传递。
解决步骤
简化允许类列表,优先使用不可变容器类
将NSMutableArray.self替换为NSArray.self,NSMutableDictionary.self替换为NSDictionary.self——解归档时,不可变容器类会自动兼容对应的可变类型,同时能正确传递内部元素的允许类。修改后的代码:let customClassArray = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, NSNull.self, NSDictionary.self, NSNumber.self, NSString.self, CustomClass.self], from: data) as? [CustomClass]检查CustomClass的归档逻辑
确认CustomClass的init?(coder:)和encode(with:)方法中,所有属性的类型都已包含在允许类列表里。比如如果类内有NSString、NSNumber类型的属性,必须确保这些类被显式加入allowedClasses。清理旧归档数据
若之前用不同的类列表归档过数据,残留数据可能引发验证异常。可以先清除UserDefaults中对应key的数据,重新归档测试:UserDefaults.standard.removeObject(forKey: "key")
额外说明
苹果在iOS 12及以上版本强化了NSKeyedUnarchiver的安全校验,即使是plist安全类型,若未显式加入允许类列表,未来版本会直接禁止解归档,因此必须确保所有涉及的类(包括容器内的元素类)都被明确添加。
内容的提问来源于stack exchange,提问作者Micro

