我正在为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] ?? []
    

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

    10-05 20:49
    查看更多