NSUserDefaults 和序列化数据

NSUserDefaults and serialized data

我对我们应用程序中的数据持久性方法有疑问。决定将 NSUserDefaults 与 NSCoding 兼容的数据模型一起使用,由于我们应用程序的规模,我不同意。

我看到的问题是当数据模型发生变化时,任何反序列化的尝试都会导致崩溃。必须卸载并重新安装该应用程序才能重新序列化。

场景:

  1. 用户安装应用程序
  2. 用户做事。
  3. 开发人员决定将 属性 添加到其中一个序列化对象并推送更新。
  4. 用户安装更新。
  5. 应用进入 'kaboom'。

发生这种情况是因为数据已使用与现在尝试反序列化的模型不同的模型进行序列化。

例子:

class Contact: NSCoding {
    var name
    var address
    var userId
}
... // NSCoding compliance happens next. This object gets serialized.

有人认为联系人需要更多内容:

class Contact: NSCoding {
    var name
    var address
    var userId
    var phoneNumber
    var emailAddress
}

尝试反序列化 Contact 对象,即使编码和解码的 NSCoding 合规性已更新为加载和反序列化,也会导致

致命错误:在展开可选值时意外发现 nil

CoreDataManager.unarchiveUser

Worker.init

所以,我的问题是,当 运行 具有不同架构的更新版本的应用程序时,我们如何才能避免发生此崩溃?

你崩溃是因为,

  1. 您正试图 decodeObject(forKey:) 一个不存在的密钥(因为在对对象进行编码时它不存在于 class 上)。这个方法returns nil.
  2. 您正在使用 ! 强制解包 #1 的结果。

根据经验,如果您的 Swift 代码在包含 ! 的行崩溃,那么 ! 是直接原因的可能性大约为 95%迷恋;撞车;崩溃。如果错误消息提到展开一个可选的,则有 100% 的机会。

decodeObject(forKey:) 方法的文档说明它可能 return nil。在您的情况下,如果您从 class 的先前版本升级并且正在解码刚刚添加的密钥,则可以保证发生这种情况。

您的代码需要认识到新属性可能没有值。最简单的修复方法是将 as! 替换为 as?。然后您将获得新 属性 的 nil 值。对于非可选属性,您可以在末尾添加类似 ?? "default value" 的内容。

您还可以使用 containsValue(forKey:) 方法在尝试解码密钥之前检查值是否存在。