NSKeyedUnarchiver在第二次运行中崩溃了swift 3

我正在尝试将自定义对象保存到UserDefaults,我将其用作源代码。 它会在get部分立即崩溃。 这是我的代码:

class Settings { static let defaults:UserDefaults = UserDefaults.standard ///VIP object: class VIP: NSObject, NSCoding { let email: String let name: String let relation: String init(email: String, name: String, relation: String) { self.email = email self.name = name self.relation = relation } required init(coder decoder: NSCoder) { self.email = decoder.decodeObject(forKey: "email") as! String self.name = decoder.decodeObject(forKey: "name") as! String self.relation = decoder.decodeObject(forKey: "relation") as! String } func encode(with coder: NSCoder) { coder.encode(email, forKey: "email") coder.encode(name, forKey: "name") coder.encode(relation, forKey: "relation") } } ///access VIPs array with this static var VIPs: [Settings.VIP] { get { ////CRASH (vips is not nil, and some amount of bytes) if let vips = self.defaults.data(forKey: "VIPs"), let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: vips) as? [Settings.VIP] { myPeopleList.forEach({print($0.email)}) return myPeopleList } else { return [] } } set { let encodedData = NSKeyedArchiver.archivedData(withRootObject: newValue) self.defaults.set(encodedData, forKey: "VIPs") } } } 

不确定我是否遗漏了一些东西,String保存和检索成功,但不是这个自定义对象。

略微重写get方法,如下所示:

 get { if let vips = self.defaults.data(forKey: "VIPs"){ print("counting: ", vips.count) if let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: vips) as? [Settings.VIP]{ myPeopleList.forEach({print($0.email)}) } } 

帮助我理解它在NSKeyedUnarchiver.unarchiveObject()部分崩溃。 但原因尚不清楚。

记录错误:

 Date/Time: 2017-09-12 21:00:43.4970 +0200 Launch Time: 2017-09-12 21:00:43.1623 +0200 OS Version: iPhone OS 10.3.3 (14G5037b) Report Version: 104 Exception Type: EXC_CRASH (SIGABRT) Exception Codes: 0x0000000000000000, 0x0000000000000000 Exception Note: EXC_CORPSE_NOTIFY Triggered by Thread: 2 Application Specific Information: abort() called Filtered syslog: None found Last Exception Backtrace: 0 CoreFoundation 0x18f0c6fe0 __exceptionPreprocess + 124 1 libobjc.A.dylib 0x18db28538 objc_exception_throw + 56 2 Foundation 0x18fb4ab0c -[NSCoder(Exceptions) __failWithException:] + 132 3 Foundation 0x18fb4acc0 -[NSCoder(Exceptions) __failWithExceptionName:errorCode:format:] + 436 4 Foundation 0x18fb16dac _decodeObjectBinary + 408 5 Foundation 0x18fb1df10 -[NSKeyedUnarchiver _decodeArrayOfObjectsForKey:] + 1544 6 Foundation 0x18fab384c -[NSArray(NSArray) initWithCoder:] + 216 7 Foundation 0x18fb17430 _decodeObjectBinary + 2076 8 Foundation 0x18fb16b68 _decodeObject + 308 9 Foundation 0x18fb15d94 +[NSKeyedUnarchiver unarchiveObjectWithData:] + 88 10 Prep 0x100097274 0x100014000 + 537204 11 Prep 0x100078384 0x100014000 + 410500 12 Prep 0x100078bd8 0x100014000 + 412632 13 Prep 0x1000a4370 0x100014000 + 590704 14 Prep 0x1001084ac 0x100014000 + 1000620 15 Prep 0x10010b180 0x100014000 + 1012096 16 Prep 0x100044d28 0x100014000 + 199976 17 TCC 0x1912d92f8 __TCCAccessRequest_block_invoke.73 + 492 18 TCC 0x1912dc3d4 __tccd_send_block_invoke + 340 19 libxpc.dylib 0x18e1baf30 _xpc_connection_reply_callout + 80 20 libxpc.dylib 0x18e1baea0 _xpc_connection_call_reply + 40 21 libdispatch.dylib 0x18df7e9a0 _dispatch_client_callout + 16 22 libdispatch.dylib 0x18df8d0d4 _dispatch_queue_override_invoke + 644 23 libdispatch.dylib 0x18df8ea50 _dispatch_root_queue_drain + 540 24 libdispatch.dylib 0x18df8e7d0 _dispatch_worker_thread3 + 124 25 libsystem_pthread.dylib 0x18e187100 _pthread_wqthread + 1096 26 libsystem_pthread.dylib 0x18e186cac start_wqthread + 4 

更新:

它只发生在第二次运行xcode,所以第一次运行 – 它工作,并尝试再次运行 – 崩溃与描述的输出。 那么存在对象必定存在一些东西?

根据https://stackoverflow.com/a/4197319/4601900问题是不允许将自定义类存储为Settings plist(NSUserDefaults)中的节点,因为数据存储在文件系统而不是来自应用程序。 设置应用程序(此数据也将可见)不知道“VIP”对象是什么。

我遇到了同样的问题,我设法解决了这个问题,将数据保存在文件中,并从保存的位置获取

我按照键动态存储2-3个值

我创建了对我有帮助的通用函数。

 let LocalServiceCacheDownloadDir = "LocalData" // Put your keys here enum WSCacheKeys:String { case Key1 = "Test1" case Key2 = "Test2" case Key3 = "Test3" } func getBaseForCacheLocal(with fileName:String) -> String? { let filePath = FileManager.default.getDocumentPath(forItemName: self.LocalServiceCacheDownloadDir) if FileManager.default.directoryExists(atPath: filePath) { return filePath.stringByAppendingPathComponent(fileName) } else { if FileManager.default.createDirectory(withFolderName: self.LocalServiceCacheDownloadDir) { return filePath.stringByAppendingPathComponent(fileName) } } return nil } //------------------------------------------------------------ func cacheDataToLocal(with Object:T,to key:WSCacheKeys) -> Bool { let success = NSKeyedArchiver.archiveRootObject(Object, toFile: getBaseForCacheLocal(with: key.rawValue)!) if success { Logger.success(s: "Local Data Cached\(String(describing: getBaseForCacheLocal(with: key.rawValue)))" as AnyObject) } else { Logger.error(s: "Sorry Failed") } return success } //------------------------------------------------------------ func loadCachedDataFromLocal(with key:WSCacheKeys ) -> [T]? { return NSKeyedUnarchiver.unarchiveObject(withFile: getBaseForCacheLocal(with: key.rawValue)!) as? [T] } //------------------------------------------------------------ 

你可以像这样获取数据

  let objects:[VIP]? = DataManager.sharedManager.loadCachedDataFromLocal(with: .Key1) 

当响应来自webservice时,我如何保存

  self.cacheDataToLocal(with: response.result.value!, to: .Key1) 

希望它对你有所帮助

我在Swift Playground上本地尝试过,效果很好。 什么都没有崩溃。 但是Xcode建议我在VIP类名之前添加@objc。 请确保我们正确地将值设置为VIP变量。

 class Settings { static let defaults:UserDefaults = UserDefaults.standard ///VIP object: @objc(_TtCC10Playground8Settings3VIP)class VIP: NSObject, NSCoding { let email: String let name: String let relation: String init(email: String, name: String, relation: String) { self.email = email self.name = name self.relation = relation } required init(coder decoder: NSCoder) { self.email = decoder.decodeObject(forKey: "email") as! String self.name = decoder.decodeObject(forKey: "name") as! String self.relation = decoder.decodeObject(forKey: "relation") as! String } func encode(with coder: NSCoder) { coder.encode(email, forKey: "email") coder.encode(name, forKey: "name") coder.encode(relation, forKey: "relation") } } ///access VIPs array with this static var VIPs: [Settings.VIP] { get { ////CRASH (vips is not nil, and some amount of bytes) if let vips = self.defaults.data(forKey: "VIPs"), let myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: vips) as? [Settings.VIP] { myPeopleList.forEach({print($0.email)}) return myPeopleList } else { return [] } } set { let encodedData = NSKeyedArchiver.archivedData(withRootObject: newValue) self.defaults.set(encodedData, forKey: "VIPs") } } } let vip = Settings.VIP.init(email: "dev.rnarendra@gmail.com", name: "naren", relation: "cool") Settings.VIPs = [vip] _ = Settings.VIPs 

XCode 9建议我隐式设置VIP类的编码名称:

在此处输入图像描述

尝试进行以下更改并重复测试:

 class Settings { static let defaults:UserDefaults = UserDefaults.standard ///VIP object: @objc(EncodedVIP)class VIP: NSObject, NSCoding { let email: String let name: String let relation: String ....