objective-c - 用户默认值从 Obj C 迁移到 Swift

标签 objective-c swift macos cocoa nsuserdefaults

我正在为我的 OS X 应用开发一个更新,它最初是用 Obj-C 编写的。
更新已用 Swift

重新编写

我正面临一个奇怪的用户默认处理问题。(因为用户首选项不能在更新中更改)

所有 native 类型(如 Bool、String)用户首选项都工作正常,但符合 NSCoding 的类无法反序列化/取消归档。它给出了一个错误:

Error :[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (KSPerson) for key (NS.objects); the class may be defined in source code or a library that is not linked

我做了以下尝试,但仍然无法找出解决方案

  1. 最初我认为这是由于不同的类名(在 Swift 它的 Person 而不是 KSPerson)。但是更改类名(改回KSPerson)并没有解决问题
  2. 为了让类(class)更像Objective C,我还尝试在类(class)前加上@objc

这是代码

let UD = NSUserDefaults.standardUserDefaults()

func serialize() {
    // Info: personList: [KSPerson]
    let encodedObject: NSData = NSKeyedArchiver.archivedDataWithRootObject(personList)
    UD.setObject(encodedObject, forKey: "key1")
    print("Serialization complete")
}

func deserialise() {
    let encodedObject: NSData = UD.objectForKey("key1") as! NSData
    let personList: [KSUrlObject] = NSKeyedUnarchiver.unarchiveObjectWithData(encodedObject) as! [KSPerson]

    for person in personList {
        print(person)
    }

注意:
我创建了 Objective C 代码的副本,并且它完美地反序列化(由原始副本序列化的内容)。
出乎我的意料。当我在 duplicate copy 中将类 KSPerson 重命名为 JSPerson 时,它给了我与上面看到的完全相同的错误

所以有一件事很清楚。您需要具有相同的类名才能取消归档 NSCoding 兼容的 objects。但这对于 Swift

来说显然是不够的

最佳答案

Swift 类包括它们的模块名称。 Objective-C 类没有。因此,当您取消存档 Objective-C 类“KSPerson”时,Swift 会查看其所有类并找到“mygreatapp.KSPerson”并得出它们不匹配的结论。

您需要告诉解档器如何将归档名称映射到模块的类名。您可以使用 setClass(_:forClassName:) 集中执行此操作:

NSKeyedUnarchiver.setClass(KSPerson.self, forClassName: "KSPerson")

这是一个全局注册,适用于所有不覆盖它的解压器。您当然可以为同一类注册多个名称以用于取消存档。例如:

NSKeyedUnarchiver.setClass(KSPerson.self, forClassName: "KSPerson")
NSKeyedUnarchiver.setClass(KSPerson.self, forClassName: "mygreatapp.Person")
NSKeyedUnarchiver.setClass(KSPerson.self, forClassName: "TheOldPersonClassName")

当你写 Swift 文件时,默认情况下它会包含模块名称。这意味着,如果您更改模块名称(或在一个模块中存档并在另一个模块中取消存档),您将无法取消存档数据。这样做有一些好处(它可以防止意外解档为错误的类型),但在许多情况下您可能不希望这样做。如果没有,您可以通过在另一个方向注册映射来覆盖它:

NSKeyedArchiver.setClassName("KSPerson", forClass:KSPerson.self)

这只会影响注册后存档的类(class)。它不会修改现有文件。

这仍然很脆弱。 unarchiveObjectWithData: 在出现 Swift 无法捕获的问题时引发 ObjC 异常。你会因为坏数据而崩溃。您可以避免使用 -decodeObjectForKey 而不是 +unarchiveObjectWithData:

let encodedObject: NSData = UD.objectForKey("key1") as? NSData ?? NSData()
let unarchiver = NSKeyedUnarchiver(forReadingWithData: encodedObject)
let personList : [KSPerson] = unarchiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? [KSPerson] ?? []

如果出现问题而不是崩溃,这将返回 []。它仍然尊重全局类名注册。

关于objective-c - 用户默认值从 Obj C 迁移到 Swift,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34412625/

相关文章:

xcode - applicationDidFinishLaunching 未调用

iphone - 如何将登录信息附加到文本文件

iphone - 日期选择器是否可以显示年/月/日 + 日名称?

iphone - unsigned int* 赋值改变目标 unsigned int

ios - 从 'String' 转换为不相关类型 'LocationModel' 始终失败警告

arrays - 在 bash 中循环遍历数组

objective-c - 如何在 header (.h) 文件中动态设置静态 "#define"变量?

ios - 在另一个 View 中内联嵌入共享图标

ios - 在 iOS 中使用 Firebase 数据库检索数据

macos - 如何在 Mac OS X 中检测蓝牙音频设备是否已连接?