NSKeyedArchiver在Swift 3(Xcode 8)中无法正常工作。

16

我已将项目迁移到Swift 3,但NSKeyedArchiver无法工作。 当我尝试像这样解码对象时,实际上会出现运行时错误:

我已将项目迁移到Swift 3,但NSKeyedArchiver无法工作。当我尝试解码对象时出现了运行时错误:

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

在Xcode 7.3中,Swift 2.2版本下完美运行。是否有其他人遇到了类似的问题?

另外,我在模拟器和设备上都遇到了这个错误。


请添加您的错误输出。 - yennsarah
2
可能是对象为 nil 或者转换为 Int 失败了。在强制转换之前,您应该查看 decodeObject 的结果并确定问题所在。另外,当处理整数时,您可以使用 decodeInteger(forKey: ) - Rob
你应该将解决方案作为一个答案添加。 - Bhumit Mehta
1
苹果真是让我们抓狂。我已经试图调试了几个小时了。这似乎难以置信,但他们竟然把decodeObject写成了Int fail!!! - zirinisp
4个回答

16

看起来这只发生在Swift 2升级到Swift 3时,当用Swift 2中的NSKeyedArchiver存档的NSData blob被Swift 3中的NSKeyedUnarchiver打开时。我的猜测是,在Swift 2中,BoolInt被编码为NSNumber,但在Swift 3中,它们被编码为原始BoolInt类型。我相信以下测试支持了这一说法:

这段代码可以在Swift 3中解档Swift 2编码的Bool,但如果Bool是在Swift 3中编码的,则返回nil:

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

在 Swift 3 中,这可以用来解档使用 Swift 3 编码的 Bool,但如果该 Bool 是由 Swift 2 编码而来,则会导致崩溃:

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

我的解决方案是:

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

谢谢,这对我有用。这是一个微小的错误,他们真的需要记录这样的东西并节省开发人员的时间。 - Ryhan
这应该是最佳答案,因为它安全地允许客户将运行Swift 2的应用程序升级到Swift 3。 - pbush25

8
我使用以下方法解决了这个问题:
decodeInteger(forKey key: String)

替换为:

decodeObject(forKey key: String)

由于某些原因,在Swift 3中,AnyObject无法转换为整数(Integer),尽管在Swift 2.2中可以。


想要提及布尔值的相同之处。 - James Kuang
我仍然遇到问题,如果从encodeObject(Swift 2)更新到decodeInteger(Swift 3),我的应用程序仍会崩溃:( - George
我刚看到这篇文章。我现在也在处理一个类似的问题:http://stackoverflow.com/questions/41224991/nskeyedarchiver-nskeyedunarchiver-swift-3-0。你也在使用NSKeyedUnarchiver吗? - Michel

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中保存和检索自定义对象


1

Swift 3:

我采用双问号(??)方法,仅在一行代码中就能完美运作。

如果val不为空,则对其进行解包并返回值。如果为空,则aDecoder.decodeInteger(forKey: "val")将被返回:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接