我正在为Update
应用程序开发OS X
,该应用程序最初是用Obj-C
编写的。
此更新已用Swift
重写
我遇到了一个用户默认值处理的奇怪问题。(因为用户首选项不能在更新中更改)
所有 native 类型(例如Bool, String
)的用户首选项都可以正常工作,但是符合NSCoding
的Class不能使用deserialse / Unarchive
。它给出了一个错误:
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
)并不能解决Objective C
,就像我也尝试在类中添加@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)
}
笔记:
我创建了一个
duplicate copy
代码的Objective C
,它完美地反序列化了(由原始副本序列化的内容)。出乎我的意料。当我将类
KSPerson
重命名为JSPerson
中的duplicate copy
时,它给了我与上述完全相同的错误所以一件事很清楚。您需要使用与Unarchive
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)
这仅影响注册后归档的类。它不会修改现有文件。
这仍然很脆弱。
unarchiveObjectWithData:
遇到问题时会引发ObjC异常,而Swift无法捕获。您将因坏数据而崩溃。您可以避免使用-decodeObjectForKey
而不是+unarchiveObjectWithData
:let encodedObject: NSData = UD.objectForKey("key1") as? NSData ?? NSData()
let unarchiver = NSKeyedUnarchiver(forReadingWithData: encodedObject)
let personList : [KSPerson] = unarchiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as? [KSPerson] ?? []
如果有问题,则返回
[]
而不是崩溃。它仍然尊重全局类名注册。