我正在为我的 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
我做了以下尝试,但仍然无法找出解决方案
- 最初我认为这是由于不同的类名(在
Swift
它的Person
而不是KSPerson
)。但是更改类名(改回KSPerson
)并没有解决问题 - 为了让类(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/