Xcode NSUserDefaults和序列化数据

Xcode NSUserDefaults和序列化数据,xcode,swift3,nsuserdefaults,nscoding,Xcode,Swift3,Nsuserdefaults,Nscoding,我对我们应用程序中的数据持久化方法有问题。决定将NSUserDefaults与NSCoding兼容的数据模型一起使用,由于我们应用程序的规模,我不同意这一点 我看到的问题是,当数据模型更改时,任何反序列化的尝试都会导致崩溃。必须卸载并重新安装应用程序才能重新序列化 场景: class Contact: NSCoding { var name var address var userId } ... // NSCoding compliance happens next.

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

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

场景

class Contact: NSCoding {
    var name
    var address
    var userId
}
... // NSCoding compliance happens next. This object gets serialized.
  • 用户安装应用程序
  • 用户做的东西
  • 开发人员决定将属性添加到其中一个序列化对象并推送更新
  • 用户安装更新
  • 应用程序会显示“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
    }
    
    尝试反序列化联系人对象,即使编码和解码的NSCoding符合性已更新为加载和反序列化,也会导致

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

    CoreDataManager.unarchiveUser

    Worker.init


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

    您崩溃的原因是

  • 您正试图对一个不存在的键进行
    解码对象(forKey:)
    (因为对象编码时该键在类中不存在)。此方法返回
    nil
  • 您正在使用
    强制展开#1的结果
  • 根据经验,如果您的Swift代码在包含
    的行上崩溃,大约有95%的可能性
    是导致崩溃的直接原因。如果错误消息中提到展开可选文件,则有100%的可能性

    decodeObject(forKey:)
    方法的文档说明它可能返回
    nil
    。在您的情况下,如果您从类的早期版本升级,并且您正在解码刚刚添加的密钥,那么这种情况肯定会发生

    您的代码需要认识到新属性可能没有值。最简单的修复方法是将
    替换为
    相同?
    。然后,您将获得新属性的
    nil
    值。对于非可选的属性,可以添加类似于
    ??“默认值”
    结束


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

    您崩溃的原因是

  • 您正试图对一个不存在的键进行
    解码对象(forKey:)
    (因为对象编码时该键在类中不存在)。此方法返回
    nil
  • 您正在使用
    强制展开#1的结果
  • 根据经验,如果您的Swift代码在包含
    的行上崩溃,大约有95%的可能性
    是导致崩溃的直接原因。如果错误消息中提到展开可选文件,则有100%的可能性

    decodeObject(forKey:)
    方法的文档说明它可能返回
    nil
    。在您的情况下,如果您从类的早期版本升级,并且您正在解码刚刚添加的密钥,那么这种情况肯定会发生

    您的代码需要认识到新属性可能没有值。最简单的修复方法是将
    替换为
    相同?
    。然后,您将获得新属性的
    nil
    值。对于非可选的属性,可以添加类似于
    ??“默认值”
    结束


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

    您是否使用核心数据?你说你没有,但你正在使用一个名为
    CoreDataManager
    的类。你需要发布NSCoding编码部分。我不认为他在使用核心数据,看起来他在将其保存到UserDefaults。UserDefaults有一个名为
    data(forKey:)
    的方法你在使用核心数据吗?你说你没有,但你正在使用一个名为
    CoreDataManager
    的类。你需要发布NSCoding编码部分。我不认为他在使用核心数据,看起来他正在将其保存到userdefaultsBTW。UserDefaults有一个名为
    data(forKey:)
    的方法,如果decoder.containsValue(forKey:“mobilePhoneNumber”)有帮助,但是让officePhoneNumber:String=decoder.decodeObject(forKey:officePhoneNumber)作为?一串“”如果你关心你的类是否有有效的数据,那就真的更好了。这里真正的解决方案是停止滥用
    UserDefaults
    ,使用真正的存储系统。我完全同意你@JonShier的观点。我正在推动系统使用更好的东西。该团队说CoreData太多了,但NSUserDefaults则是相反的极端。我在想,围绕sqlite3的包装器,比如FMDB,将是一个很好的快乐媒介。也许他们会喜欢Realm?这取决于你需要储存什么。只有在需要性能时,我才会回退到SQLite。更高级别的东西可能会更好。领域?我没听说过。谢谢你的建议。如果decoder.containsValue(forKey:“mobilePhoneNumber”)有帮助,但是让officePhoneNumber:String=decoder.decodeObject(forKey:“officePhoneNumber”)作为?一串“”如果你关心你的类是否有有效的数据,那就真的更好了。这里真正的解决方案是停止滥用
    UserDefaults
    ,使用真正的存储系统。我完全同意你@JonShier的观点。我正在推动系统使用更好的东西。该团队说CoreData太多了,但NSUserDefaults则是相反的极端。我在想围绕sqlite3的包装器,比如FMDB,将是一个不错的选择