gpt4 book ai didi

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

转载 作者:搜寻专家 更新时间:2023-10-31 22:05:30 26 4
gpt4 key购买 nike

我有一个复杂的类,其中有许多嵌套类,深达几层,它被序列化并存储在 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)")
}

}
}

现在通过选择测试用例本身在一个 session 中运行两个测试。这将按预期正确序列化和反序列化对象。

现在尝试独立运行两个单元测试。先连载。反序列化。第二次测试失败

failed: caught "NSInvalidUnarchiveOperationException", "*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (_TtCC17SerializationDemo3Foo3Bar) for key (bar); the class may be defined in source code or a library that is not linked"

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

最佳答案

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

在示例测试中添加一行

_ = Foo(x: 0)

testDeserialise() 解决了这个问题。显然是通过预加载必要的类定义。

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

基本上:

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

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

更新

我已向 Apple 提交错误报告 #30034842

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

他们还注意到:

You should also assign Bar an @objc name (e.g. @objc(Bar)); we do not guarantee the stability of Swift mangled names over time — for backwards and forwards compatibility, you should give it a canonical name.

更新 2018 年 3 月

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

关于swift - 无法使用 NSCoding 反序列化嵌套类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41757086/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com