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")