我正在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
代码,并deserialised完美(什么是由原件连载).
出乎我的意料.当我将类重命名KSPerson
为JSPerson
in时duplicate copy
,给出了与上面完全相同的错误
所以有一件事是清楚的.您需要具有与Unarchive NSCoding
兼容的相同类名objects
.但这显然是不够的Swift
Swift类包括它们的模块名称.Objective-C类没有.因此,当您取消归档Objective-C类"KSPerson"时,Swift会查看其所有类并查找"mygreatapp.KSPerson",并得出结论它们不匹配.
您需要告诉unarchiver如何将存档名称映射到模块的类名.你可以集中使用setClass(_:forClassName:)
:
NSKeyedUnarchiver.setClass(KSPerson.self, forClassName: "KSPerson")
这是一个全局注册,适用于所有未覆盖它的unarchiver.您当然可以为同一个类注册多个名称以进行取消归档.例如:
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] ?? []
这将返回[]
如果有一个问题,而不是崩溃.它仍然尊重全球类名注册.