当前位置:  开发笔记 > IOS > 正文

无法使用NSCoding反序列化嵌套类

如何解决《无法使用NSCoding反序列化嵌套类》经验,为你挑选了1个好方法。

我有一个复杂的类,有很多嵌套类到多个深层,它们被序列化并存储在CoreData表中.问题是,由于升级到Swift 3类的实例无法反序列化,因此在尝试解码嵌套类的实例时失败.

为了说明问题,使用实现NSCoding的内部类创建一个类定义:

进口基金会

class Foo : NSObject, NSCoding {

    class Bar : NSObject, NSCoding {
        var x : Int

        init(x:Int) {
            self.x = x
        }

        required init?(coder aDecoder: NSCoder) {
            self.x = aDecoder.decodeInteger(forKey: "x")
        }

        func encode(with aCoder: NSCoder) {
            aCoder.encode(x, forKey:"x")
        }
    }

    var bar:Bar

    init(x:Int) {
        self.bar = Bar(x: x)
    }

    required init?(coder aDecoder: NSCoder) {
        self.bar = aDecoder.decodeObject(forKey: "bar") as! Bar
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(bar, forKey:"bar")
    }
}

然后构建单元测试:

import XCTest
@testable import SerializationDemo

class SerializationDemoTests: XCTestCase {

     func fileURL() -> URL {

        let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        return dir.appendingPathComponent("foo.dat")

    }

    func testSerialize() {
        let foo = Foo(x:42)
        let data = NSKeyedArchiver.archivedData(withRootObject: foo)

        let url = fileURL()

        do {
            try data.write(to: url)
        } catch {
            XCTFail("\(error)")
        }
    }

    func testDeserialise() {
        let url = fileURL()
        do {
            let data = try Data(contentsOf: url)
            let myfoo = NSKeyedUnarchiver.unarchiveObject(with: data) as! Foo
            XCTAssertEqual(myfoo.bar.x, 42)
        } catch {
            XCTFail("\(error)")
        }

    }
}

现在通过选择测试用例本身在一个会话中运行两个测试.这将按预期正常工作,正确地序列化和反序列化对象.

现在尝试独立运行两个单元测试.首先序列化.反序列化.第二次测试失败了

失败:捕获"NSInvalidUnarchiveOperationException","*** - [NSKeyedUnarchiver decodeObjectForKey:]:无法解码类(_TtCC17SerializationDemo3Foo3Bar)对象的密钥(bar);该类可以在源代码中定义,也可以在未链接的库中定义"

一种解决方案是重构代码,以便所有内部类都是顶级的,这也需要重命名这些类以使它们在应用程序中全局唯一.我宁愿不用这种方式重做代码



1> Dale..:

我确定这是Swift 3中的一个错误,可能是尝试优化类加载器.在通过其他方式加载类定义之前,类加载器似乎不会在反序列化期间直接加载内部类.

在样本测试中添加一行

_ = Foo(x: 0)

to testDeserialise()修复了这个问题.显然是通过预加载必要的类定义.

我能够通过向复杂类添加一些代码来修复我的应用程序中的问题,以便在第一次尝试反序列化实例时递归地预加载每个内部类的实例:

基本上:

if !preloaded {
   for x in  allElements() {
       _ = x.allOperations()
   }
   preloaded = true
}

allElements()返回一个实例化每个内部类的一个实例的列表,allOperations()返回每个内部类的所有内部类.

UPDATE

我已经向Apple#30034842提交了一份错误报告

Apple工程师也提出了另一种临时解决方法.NSCoder.decodeObject(of: [AnyClass]?, forKey: String)在解码对象知道可以解码的可能类时使用.

他们还指出:

您还应该为Bar指定@objc名称(例如@objc(Bar)); 我们不保证随着时间的推移Swift受损名称的稳定性 - 为了向后兼容,你应该给它一个规范的名称.

更新 2018年3月

这在Xcode 9中得到修复,即使在使用Swift语言版本3.2进行编译时也是如此.此外,在编码期间在运行时强制分配@objc名称.

推荐阅读
mobiledu2402851377
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有