可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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")