NSKeyedArchiver не работает в Swift 3 (XCode 8)
Я перенес свой проект в Swift 3 и NSKeyedArchiver
не работает. У меня на самом деле есть ошибка времени выполнения при попытке декодирования объекта следующим образом:
let startDayTime = aDecoder.decodeObject(forKey: Key.startDayTime) as! Int
Он отлично работал в Swift 2.2 в Xcode 7.3. Кто-нибудь еще сталкивался с такими проблемами?
P.S. У меня есть эта ошибка как для Simulator, так и для устройства.
UPDATE: я решил эту проблему, используя decodeInteger(forKey key: String)
вместо decodeObject(forKey key: String)
. По какой-то причине AnyObject не бросает Integer в Swift 3, хотя это произошло в Swift 2.2
Ответы
Ответ 1
Похоже, что это происходит только на границе обновления Swift 2 до Swift 3, когда кадр NSData, заархивированный с NSKeyedArchiver
в Swift 2, открывается с помощью NSKeyedUnarchiver
в Swift 3. Я предполагаю, что в Swift 2, Bool
и Int
кодируются как NSNumber
, но в Swift 3 они кодируются как raw Bool
и Int
. Я считаю, что следующий тест подтверждает это утверждение:
Это работает в Swift 3 для разблокирования a Bool
, закодированного в Swift 2, но возвращает nil
, если Bool был закодирован в Swift 3:
let visible = aDecoder.decodeObject(forKey: "visible") as? Bool
Это работает в Swift 3 для разблокировки a Bool
, закодированного в Swift 3, но сбой, если Bool был закодирован в Swift 2:
let visible = aDecoder.decodeBool(forKey: "visible")
Мое решение:
let visible = aDecoder.decodeObject(forKey: "visible") as? Bool ?? aDecoder.decodeBool(forKey: "visible")
Ответ 2
UPDATE: я решил эту проблему, используя decodeInteger (ключ forKey: String) вместо decodeObject (ключ forKey: String). По какой-то причине AnyObject не бросает Integer в Swift 3, хотя это произошло в Swift 2.2
Ответ 3
Вот решение:
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")
}}
Как использовать:
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()
}}
Ссылка: Swift 3 сохранение и извлечение пользовательского объекта из userDefaults
Ответ 4
Swift 3:
Я принимаю двойной вопросительный знак (??), и он работает как шарм только в одной строке.
Если значение val не равно нулю, оно разворачивается и возвращается значение. Если он равен нулю, возвращается aDecoder.decodeInteger(forKey: "val")
:
self.val = aDecoder.decodeObject(forKey: "val") as? Int ?? aDecoder.decodeInteger(forKey: "val")