NSKeyedArchiver does not work in Swift 3 ( XCode 8)

匿名 (未验证) 提交于 2019-12-03 02:48:02

问题:

I have migrated my project to Swift 3 and NSKeyedArchiver does not work. I actually have a runtime error when trying to decode object like this:

let startDayTime = aDecoder.decodeObject(forKey: Key.startDayTime) as! Int 

It worked perfectly in Swift 2.2 in Xcode 7.3. Has anybody else faced such troubles?

P.S. I have this error on both Simulator and Device.

UPDATE: I solved this problem by using decodeInteger(forKey key: String) instead of decodeObject(forKey key: String). By some reason AnyObject does not cast to Integer in Swift 3 though it did in Swift 2.2

回答1:

It appears that this only happens on the Swift 2 to Swift 3 update boundary when a NSData blob archived with a NSKeyedArchiver in Swift 2 is opened with a NSKeyedUnarchiver in Swift 3. My guess is that on Swift 2, the Bool and Int are encoded as NSNumber, but in Swift 3, they are encoded as raw Bool and Int types. I believe the following test supports this claim:

This works in Swift 3 to unarchive a Bool encoded in Swift 2, but returns nil if the Bool was encoded in Swift 3:

let visible = aDecoder.decodeObject(forKey: "visible") as? Bool 

This works in Swift 3 to unarchive a Bool encoded in Swift 3, but crashes if the Bool was encoded in Swift 2:

let visible = aDecoder.decodeBool(forKey: "visible") 

My solution is:

let visible = aDecoder.decodeObject(forKey: "visible") as? Bool ?? aDecoder.decodeBool(forKey: "visible") 


回答2:

UPDATE: I solved this problem by using decodeInteger(forKey key: String) instead of decodeObject(forKey key: String). By some reason AnyObject does not cast to Integer in Swift 3 though it did in Swift 2.2



回答3:

Here is solution:

class Person: NSObject, NSCoding { let name: String let age: Int required init(name: String, age: Int) {     self.name = name     self.age = age } required init(coder decoder: NSCoder) {     self.name = decoder.decodeObject(forKey: "name") as? String ?? ""     self.age = decoder.decodeInteger(forKey: "age") }  func encode(with coder: NSCoder) {     coder.encode(name, forKey: "name")     coder.encode(age, forKey: "age") }} 

How to use:

class ViewController: UIViewController { override func viewDidLoad() {     super.viewDidLoad()     let newPerson = Person(name: "Joe", age: 10)     var people = [Person]()     people.append(newPerson)     let encodedData = NSKeyedArchiver.archivedData(withRootObject: people)     UserDefaults.standard().set(encodedData, forKey: "people")     if let data = UserDefaults.standard().data(forKey: "people"),         myPeopleList = NSKeyedUnarchiver.unarchiveObject(with: data) as? [Person] {         myPeopleList.forEach({print( $0.name, $0.age)})  // Joe 10     } else {         print("There is an issue")     } } override func didReceiveMemoryWarning() {     super.didReceiveMemoryWarning() }} 

Reference: Swift 3 saving and retrieving custom object from userDefaults



回答4:

Swift 3:

I adopt the double question mark (??) method and it work like a charm in only one line.

If val is not nil than it is unwrapped and the value returned. If it is nil then aDecoder.decodeInteger(forKey: "val") returned:

self.val = aDecoder.decodeObject(forKey: "val") as? Int ?? aDecoder.decodeInteger(forKey: "val") 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!