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. У меня есть эта ошибка как на симуляторе, так и на устройстве.

ОБНОВЛЕНИЕ: я решил эту проблему, используя decodeInteger(forKey key: String) вместо decodeObject(forKey key: String). По какой-то причине AnyObject не приводит к Integer в Swift 3, хотя это было в Swift 2.2.


person Arthur Shkil    schedule 30.06.2016    source источник
comment
Пожалуйста, добавьте свой вывод ошибки.   -  person yennsarah    schedule 30.06.2016
comment
Это может быть либо объект nil, либо приведение к Int не удалось. Вы должны посмотреть на результат decodeObject до принудительного приведения и посмотреть, в чем проблема. Кроме того, к вашему сведению, при работе с целыми числами вы можете использовать decodeInteger(forKey: ).   -  person Rob    schedule 30.06.2016
comment
Вы должны добавить решение в качестве ответа   -  person Bhumit Mehta    schedule 05.07.2016
comment
Apple играет на наших нервах. Я пытаюсь отладить это уже пару часов. Кажется невероятным, но они должны были сделать decodeObjece как? Инт провал!!!!   -  person zirinisp    schedule 03.10.2016


Ответы (4)


Похоже, что это происходит только на границе обновления Swift 2 и Swift 3, когда большой двоичный объект NSData, заархивированный с помощью NSKeyedArchiver в Swift 2, открывается с помощью NSKeyedUnarchiver в Swift 3. Я предполагаю, что в Swift 2 Bool и Int кодируются как NSNumber, но в Swift 3 они кодируются как необработанные типы Bool и Int. Я считаю, что следующий тест подтверждает это утверждение:

Это работает в Swift 3 для разархивирования Bool, закодированного в Swift 2, но возвращает nil, если Bool был закодирован в Swift 3:

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

Это работает в Swift 3 для разархивирования Bool, закодированного в Swift 3, но происходит сбой, если Bool был закодирован в Swift 2:

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

Мое решение:

let visible = aDecoder.decodeObject(forKey: "visible") as? Bool ?? aDecoder.decodeBool(forKey: "visible")
person James Kuang    schedule 14.10.2016
comment
спасибо, это сработало для меня. такая тонкая ошибка, им действительно нужно документировать такие вещи и экономить время разработчиков. - person Ryhan; 29.01.2017
comment
Это должен быть лучший ответ, потому что он позволяет безопасно обновлять приложения, работающие под управлением Swift 2 до 3. - person pbush25; 03.02.2017

ОБНОВЛЕНИЕ: я решил эту проблему, используя decodeInteger (ключ forKey: String) вместо decodeObject (ключ forKey: String). По какой-то причине AnyObject не приводит к Integer в Swift 3, хотя это было в Swift 2.2.

person Arthur Shkil    schedule 20.07.2016
comment
Хочу отметить то же самое для Bool. - person James Kuang; 13.10.2016
comment
У меня все еще возникают проблемы, если обновление из encodeObject (swift 2), decodeInteger (swift 3) по-прежнему приводит к сбою моего приложения :( - person George; 12.11.2016
comment
Я только что нашел этот пост. Сейчас я имею дело с несколько похожей проблемой здесь: stackoverflow. ком/вопросы/41224991/. Вы также использовали NSKeyedUnarchiver. - person Michel; 20.12.2016

Вот решение:

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

person Amit Soni    schedule 28.11.2016

Свифт 3:

Я использую двойной вопросительный знак (??), и он прекрасно работает всего в одной строке.

Если val не равен нулю, то он разворачивается и возвращается значение. Если он равен нулю, то возвращается aDecoder.decodeInteger(forKey: "val"):

self.val = aDecoder.decodeObject(forKey: "val") as? Int ?? aDecoder.decodeInteger(forKey: "val")
person Alessandro Ornano    schedule 15.10.2016