NSKeyedArchiver 并在目标之间共享自定义类

Chr*_*ris 2 nskeyedarchiver ios nskeyedunarchiver swift ios-app-extension

我的应用程序使用自定义类作为其数据模型:

class Drug: NSObject, NSCoding {
    // Properties, methods etc...
}
Run Code Online (Sandbox Code Playgroud)

我刚刚创建了一个 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]
}
Run Code Online (Sandbox Code Playgroud)

我遇到了类命名空间的问题,其中我的 Today 扩展中的 NSKeyedUnarchiver 无法正确解码对象,所以我使用了这个答案@objc在类定义之前添加:

@objc(Drug)
class Drug: NSObject, NSCoding {
    // Properties, methods etc...
}
Run Code Online (Sandbox Code Playgroud)

这样就完美解决了问题。但是,这将是我的应用程序的 1.3 版,这似乎破坏了预先存在的数据的取消归档过程(正如我认为的那样)。

处理这种情况的最佳方法是什么,好像我只是进行了这个更改,新版本的应用程序对于现有用户来说会崩溃!

我找不到关于此的任何其他答案,我不确定该NSKeyedArchiver.setClass()方法是否相关,也不确定在哪里使用它。

任何帮助将不胜感激。谢谢。

Ita*_*ber 8

这正是用例NSKeyedUnarchiver.setClass(_:forClassName:)- 您可以通过将当前类与其旧名称相关联来向前迁移旧类:

let unarchiver = NSKeyedUnarchiver(...)
unarchiver.setClass(Drug.self, forClassName: "myApp.Drug")
// unarchive as appropriate
Run Code Online (Sandbox Code Playgroud)

或者,您可以提供符合NSKeyedUnarchiverDelegate并提供 的委托unarchiver(_:cannotDecodeObjectOfClassName:originalClasses:),但这对于这种情况可能有点过分。


请记住,这适用于向前迁移旧数据。如果您有新版本的应用程序需要将数据发送到旧版本的应用程序,您需要在存档端做类似的事情 - 继续使用旧名称对新类进行编码NSKeyedArchiver.setClassName(_:for:)

let archiver = NSKeyedArchiver(...)
archiver.setClassName("myApp.Drug", for: Drug.self)
// archive as appropriate
Run Code Online (Sandbox Code Playgroud)

如果您遇到此向后兼容性问题,那么不幸的是,只要有旧版本应用程序的用户,您就可能需要继续使用旧名称。

  • @AlejandroViquez 无论您实际上要继续存档或取消存档数据。因此,无论在哪个例程中处理将数据保存到磁盘或协调它。这将根据您在应用程序中的设置方式而有所不同。或者,`NSKeyedArchiver` 和 `NSKeyedUnarchiver` 有类方法来做同样的事情(例如 [`NSKeyedArchiver.setClass(_:forClassName:)`](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/1409718 -setclass?language=objc) 影响应用程序中的所有档案,您可以从 AppDelegate 或类似调用 (2认同)