将数据解归档为CNContact时发生崩溃问题求助
问题场景
将数据解归档为CNContact时触发崩溃,崩溃发生在以下代码行:
var decodedContacts = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [CNContact.self], from: archived) as? [CNContact?]
错误日志
NSKeyedUnarchiver.unarchivedObject(ofClasses: [CNContact.self], from: contactsData) as? [CNContact?] Error Domain=NSCocoaErrorDomain Code=4864 "value for key 'root' was of unexpected class 'NSArray' (0x1d97a9ed8) [/System/Library/Frameworks/CoreFoundation.framework]. Allowed classes are: {( "'CNContact' (0x1d9881018) [/System/Library/Frameworks/Contacts.framework]" )}" UserInfo={NSDebugDescription=value for key 'root' was of unexpected class Allowed classes are: {( "'CNContact' (0x1d9881018) [/System/Library/Frameworks/Contacts.framework]" )}}
关联代码实现
import Contacts class ContactsManager { static let numberOfSavedContacts = 12 static let shared = ContactsManager() private init() { loadContacts() } private(set) var contacts: [CNContact?] = Array(repeating: nil, count: numberOfSavedContacts) func add(contact: CNContact, at index: Int, phone: CNLabeledValue<CNPhoneNumber>? = nil) { if index < ContactsManager.numberOfSavedContacts { let editContact = contact.mutableCopy() as! CNMutableContact if phone != nil { editContact.phoneNumbers = [phone!] } contacts[index] = editContact saveContacts() } } func deleteContact(_ index: Int) { if index < ContactsManager.numberOfSavedContacts { contacts[index] = nil saveContacts() } } func saveContacts() { do { let contactsData = try NSKeyedArchiver.archivedData(withRootObject:contacts,requiringSecureCoding:false) UserDefaults.standard.set(contactsData, forKey: "contacts") } catch { print(error) } } func loadContacts() { if let contactsData = UserDefaults.standard.object(forKey: "contacts") as? Data { do { if var decodedContacts = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, CNContact.self, NSNull.self], from: contactsData) as? [CNContact?] { print(decodedContacts) contacts = decodedContacts } else { print("decodedContacts is nil") } } catch { print(error) } } } }
解决方法
崩溃核心原因是解归档时未允许根对象类型(NSArray),同时数组包含nil元素,归档时nil会被转为NSNull,因此也需要允许该类型。
修改解归档代码,确保ofClasses参数包含所有必需类型:
// 正确的解归档写法 if var decodedContacts = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, CNContact.self, NSNull.self], from: contactsData) as? [CNContact?] { contacts = decodedContacts }
额外注意事项:
- 你归档的根对象是
[CNContact?]类型的数组,对应OC的NSArray,必须将其加入允许类列表 - 数组中的
nil会被序列化为NSNull,所以要把NSNull.self也加入允许类 - 如果之前用错误的类列表归档过数据,建议清理旧的UserDefaults缓存,避免解归档冲突
你的loadContacts方法中已经写了正确的允许类列表,但崩溃的代码行仅传入了[CNContact.self],这是问题根源,确保所有解归档逻辑都使用正确的类列表即可解决崩溃。
内容的提问来源于stack exchange,提问作者Roei Nadam

