我正在为Update
应用OS X
工作,该应用最初是用Obj-C
编写的。
此更新已在Swift
我遇到一个奇怪的用户默认值处理问题。(因为更新时不得更改用户首选项)
所有原生类型(如Bool, String
)用户首选项都正常运行,但符合NSCoding
的类无法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
答案 0 :(得分:12)
Swift类包括它们的模块名称。 Objective-C类没有。因此,当您取消归档Objective-C类" KSPerson"时,Swift将查看其所有类并查找" mygreatapp.KSPerson"并得出结论,他们不匹配。
您需要告诉unarchiver如何将存档名称映射到模块的类名。您可以使用setClass(_:forClassName:)
集中进行此操作:
NSKeyedUnarchiver.setClass(KSPerson.self, forClassName: "KSPerson")
这是一项全球注册,适用于所有不会覆盖它的unarchivers。您当然可以为同一个类注册多个名称以进行取消归档。例如:
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] ?? []
如果出现问题而不是崩溃,则会返回[]
。它仍然尊重全球类名注册。