NSKeyedArchiver 并在目标之间共享自定义类
Posted
技术标签:
【中文标题】NSKeyedArchiver 并在目标之间共享自定义类【英文标题】:NSKeyedArchiver and sharing a custom class between targets 【发布时间】:2018-07-13 00:26:29 【问题描述】:我的应用使用自定义类作为其数据模型:
class Drug: NSObject, NSCoding
// Properties, methods etc...
我刚刚创建了一个 Today 扩展,需要从中访问用户的数据,所以我使用 NSCoding 将我的数据保存在应用容器和共享容器中。这些是主应用程序中的保存和加载功能:
func saveDrugs()
// Save to app container
let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(drugs, toFile: Drug.ArchiveURL.path)
if isSuccessfulSave
print("Drugs successfully saved locally")
else
print("Error saving drugs locally")
// Save to shared container for extension
let isSuccessfulSaveToSharedContainer = NSKeyedArchiver.archiveRootObject(drugs, toFile: Drug.SharedArchiveURL.path)
if isSuccessfulSaveToSharedContainer
print("Drugs successfully saved to shared container")
else
print("Error saving drugs to shared container")
func loadDrugs() -> [Drug]?
return NSKeyedUnarchiver.unarchiveObject(withFile: Drug.ArchiveURL.path) as? [Drug]
我遇到了类命名空间问题,我的 Today 扩展中的 NSKeyedUnarchiver 无法正确解码对象,所以我使用了this answer 并在类定义前添加了@objc
:
@objc(Drug)
class Drug: NSObject, NSCoding
// Properties, methods etc...
这完美地解决了这个问题。但是,这将是我的应用程序的 1.3 版,这似乎破坏了预先存在的数据的取消归档过程(我认为它可能会这样)。
处理这种情况最好的方法是什么,好像我只是做了这个改变,新版本的应用程序会为现有用户崩溃!
我找不到任何其他答案,我不确定NSKeyedArchiver.setClass()
方法是否相关,也不确定在哪里使用它。
我们将不胜感激地接受任何帮助。谢谢。
【问题讨论】:
应用为什么会崩溃? 嗨@matt 它崩溃了,因为一旦我添加了@objc(Drug) 行,NSKeyedUnarchiver 就无法解码旧的 Drug 类 (myApp.Drug) - 该类现在变为 *.Drug,它可以工作在应用程序和扩展程序之间共享时,但不解码旧的用户数据。新用户就好了。 【参考方案1】:这正是NSKeyedUnarchiver.setClass(_:forClassName:)
的用例——您可以通过关联当前类的旧名称来向前迁移旧类:
let unarchiver = NSKeyedUnarchiver(...)
unarchiver.setClass(Drug.self, forClassName: "myApp.Drug")
// unarchive as appropriate
或者,您可以提供一个符合 NSKeyedUnarchiverDelegate
的委托并提供一个 unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:)
,但这对于这种情况可能有点过头了。
请记住,这适用于向前迁移旧数据。如果您有更新版本的应用程序需要将数据发送到旧版本的应用程序,您需要在存档方面做类似的事情 - 继续使用 NSKeyedArchiver.setClassName(_:for:)
使用旧名称对新类进行编码:
let archiver = NSKeyedArchiver(...)
archiver.setClassName("myApp.Drug", for: Drug.self)
// archive as appropriate
如果您遇到此向后兼容性问题,那么很遗憾,只要有旧版本应用的用户,您可能需要继续使用旧名称。
【讨论】:
谢谢你!我只需要取消归档,因为我的 Today 扩展只需要读取数据而不是写入数据。一旦用户升级了应用程序并加载了他们存储的数据,它将以新的方式重新存档。澄清一下,您的实现是否会取消归档新的 (@objc(Drug)) 类和旧的类?我还需要使用@objc 吗? 我现在不在我的 Mac 上,但我稍后会对此进行测试并将你的标记为正确答案。谢谢。 @Ampoule 您仍然需要为该类指定一个@objc
名称以使其在两个目标之间保持稳定,因此请保留 @objc(Drug)
。为现有的类名设置一个类是附加的——它添加了一个从 "myApp.Drug"
到 Drug
的映射,并允许您将 "Drug"
取消归档。
我试过这个,但它不会编译,因为我试图使用 unarchiver 实例的 unArchiveObject 方法,但它是一个静态方法,所以需要在 NSKeyedUnarchiver 上调用。但是,我该如何使用 setClass?我可以先做 NSKeyedUnarchiver.setClass(...) 然后 NSKeyedUnarchiver.unarchiveObject(...) 吗?
@AlejandroViquez 无论您打算在哪里实际继续存档或取消存档数据。因此,无论哪种例程处理将数据保存到磁盘或协调它。这将根据您在应用程序中的设置方式而有所不同。或者,NSKeyedArchiver
和 NSKeyedUnarchiver
具有执行相同操作的类方法(例如,NSKeyedArchiver.setClass(_:forClassName:)
会影响应用程序中的所有存档,您可以从 AppDelegate 或类似方法调用以上是关于NSKeyedArchiver 并在目标之间共享自定义类的主要内容,如果未能解决你的问题,请参考以下文章
尝试使用 NSKeyedArchiver 在 UserDefaults 中保存自定义对象
如何使用 NSKeyedArchiver 对自定义类进行编码和解码