NSKeyedArchiver 在 Swift 3 (Xcode 8) 中不起作用

Posted

技术标签:

【中文标题】NSKeyedArchiver 在 Swift 3 (Xcode 8) 中不起作用【英文标题】:NSKeyedArchiver does not work in Swift 3 (Xcode 8) 【发布时间】:2016-06-30 13:23:35 【问题描述】:

我已将我的项目迁移到 Swift 3 并且 NSKeyedArchiver 不起作用。在尝试像这样解码对象时,我实际上遇到了运行时错误:

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

它在 Xcode 7.3 中的 Swift 2.2 中完美运行。有没有其他人遇到过这样的麻烦?

附:我在模拟器和设备上都有这个错误。

【问题讨论】:

请添加您的错误输出。 可能是对象为nil 或转换为Int 失败。您应该在强制转换之前查看decodeObject 的结果,看看哪个是问题所在。另外,仅供参考,在处理整数时,您可以使用decodeInteger(forKey: ) 您应该添加解决方案作为答案 Apple 在玩弄我们的神经。我一直在尝试调试这个几个小时。这似乎令人难以置信,但他们不得不让 decodeObjece 成为?内部失败!!!! 【参考方案1】:

这似乎只发生在 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")

【讨论】:

谢谢,这对我有用。如此微妙的错误,他们真的需要记录这样的东西并节省开发人员的时间。 这应该是最佳答案,因为它允许客户安全地将运行 swift 2 的应用升级到 3。【参考方案2】:

我解决了这个问题:

decodeInteger(forKey key: String)

代替:

decodeObject(forKey key: String)

由于某种原因 AnyObject 在 Swift 3 中没有强制转换为 Integer,尽管它在 Swift 2.2 中进行了

【讨论】:

想为 Bool 提同样的问题。 如果从 encodeObject (swift 2)、decodeInteger (swift 3) 更新仍然会导致我的应用程序崩溃:( 我刚找到这篇文章。我现在在这里处理一个有点类似的问题:***.com/questions/41224991/…。您是否也在使用 NSKeyedUnarchiver。【参考方案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 saving and retrieving custom object from userDefaults

【讨论】:

【参考方案4】:

Swift 3

我采用双问号 (??) 方法,它就像一个魅力只有一行

如果 val 不是 nil ,则将其解包并返回值。如果为 nil,则返回 aDecoder.decodeInteger(forKey: "val")

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

【讨论】:

以上是关于NSKeyedArchiver 在 Swift 3 (Xcode 8) 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

NSKeyedArchiver 返回 nil Swift 4.2

Swift NSKeyedArchiver toFile:未解析的标识符

Swift iOS ARKit 从文件 NSKeyedUnarchiver NSKeyedArchiver 加载 ARWorldMap 数据

在 Swift 中编码对象

iOS NSKeyedArchiver(轻量级缓存)

如何在 Swift 中捕获 NSKeyedUnarchiver“NSInvalidUnarchiveOperationException”?