Art*_*KGH 5 nscoding swift swift4 codable
在测试新的 Codable 如何与 NSCoding 交互时,我使用包含 Codable 结构的 Class 进行了涉及 NSCoding 的操场测试。丝毫
struct Unward: Codable {
var id: Int
var job: String
}
class Akward: NSObject, NSCoding {
var name: String
var more: Unward
init(name: String, more: Unward) {
self.name = name
self.more = more
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(more, forKey: "more")
}
required init?(coder aDecoder: NSCoder) {
name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
more = aDecoder.decodeObject(forKey: "more") as? Unward ?? Unward(id: -1, job: "unk")
super.init()
}
}
var upone = Unward(id: 12, job: "testing")
var adone = Akward(name: "Adrian", more: upone)
Run Code Online (Sandbox Code Playgroud)
以上内容被 Playground 接受,不会产生任何编译器错误。
但是,如果我尝试保存完成,如下所示:
let encodeit = NSKeyedArchiver.archivedData(withRootObject: adone)
Run Code Online (Sandbox Code Playgroud)
操场立即崩溃并出现错误:
错误:执行被中断,原因:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)。
为什么?有没有办法让 NSCoding 类包含 Codable 结构?
现有的答案并没有真正解决互操作的问题,而是展示了如何从 迁移NSCoding到Codable.
我有一个用例,这不是一个选项,而且我确实需要NSCoding从Codable上下文中使用。如果您好奇:我需要在我的 Mac 应用程序的 XPC 服务之间发送模型,并且这些模型包含NSImages. 我本可以制作一堆 DTO 来序列化/反序列化图像,但这会是很多样板。此外,这是属性包装器的完美用例。
这是我想出的属性包装:
@propertyWrapper
struct CodableViaNSCoding<T: NSObject & NSCoding>: Codable {
struct FailedToUnarchive: Error { }
let wrappedValue: T
init(wrappedValue: T) { self.wrappedValue = wrappedValue }
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let data = try container.decode(Data.self)
let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data)
unarchiver.requiresSecureCoding = Self.wrappedValueSupportsSecureCoding
guard let wrappedValue = T(coder: unarchiver) else {
throw FailedToUnarchive()
}
unarchiver.finishDecoding()
self.init(wrappedValue: wrappedValue)
}
func encode(to encoder: Encoder) throws {
let archiver = NSKeyedArchiver(requiringSecureCoding: Self.wrappedValueSupportsSecureCoding)
wrappedValue.encode(with: archiver)
archiver.finishEncoding()
let data = archiver.encodedData
var container = encoder.singleValueContainer()
try container.encode(data)
}
private static var wrappedValueSupportsSecureCoding: Bool {
(T.self as? NSSecureCoding.Type)?.supportsSecureCoding ?? false
}
}
Run Code Online (Sandbox Code Playgroud)
这是我为其编写的简单测试:
import Quick
import Nimble
import Foundation
@objc(FooTests_SampleNSCodingClass)
private class SampleNSCodingClass: NSObject, NSCoding {
let a, b, c: Int
init(a: Int, b: Int, c: Int) {
self.a = a
self.b = b
self.c = c
}
required convenience init?(coder: NSCoder) {
self.init(
a: coder.decodeInteger(forKey: "a"),
b: coder.decodeInteger(forKey: "b"),
c: coder.decodeInteger(forKey: "c")
)
}
func encode(with coder: NSCoder) {
coder.encode(a, forKey: "a")
coder.encode(b, forKey: "b")
coder.encode(c, forKey: "c")
}
}
@objc(FooTests_SampleNSSecureCodingClass)
private class SampleNSSecureCodingClass: SampleNSCodingClass, NSSecureCoding {
static var supportsSecureCoding: Bool { true }
}
private struct S<T: NSObject & NSCoding>: Codable {
@CodableViaNSCoding
var sampleNSCodingObject: T
}
class CodableViaNSCodingSpec: QuickSpec {
override func spec() {
context("Used with a NSCoding value") {
let input = S(sampleNSCodingObject: SampleNSCodingClass(a: 123, b: 456, c: 789))
it("round-trips correctly") {
let encoded = try JSONEncoder().encode(input)
let result = try JSONDecoder().decode(S<SampleNSCodingClass>.self, from: encoded)
expect(result.sampleNSCodingObject.a) == 123
expect(result.sampleNSCodingObject.b) == 456
expect(result.sampleNSCodingObject.c) == 789
}
}
context("Used with a NSSecureCoding value") {
let input = S(sampleNSCodingObject: SampleNSSecureCodingClass(a: 123, b: 456, c: 789))
it("round-trips correctly") {
let encoded = try JSONEncoder().encode(input)
let result = try JSONDecoder().decode(S<SampleNSSecureCodingClass>.self, from: encoded)
expect(result.sampleNSCodingObject.a) == 123
expect(result.sampleNSCodingObject.b) == 456
expect(result.sampleNSCodingObject.c) == 789
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
一些注意事项:
如果您需要采用其他方式(将Codable对象嵌入NSCoding存档中),您可以使用添加到NSCoder/的现有方法NSDecoder
这将为每个对象创建一个新的存档。除了在编码/解码期间添加相当多的对象分配之外,它还可能使结果膨胀(在我的测试中,空存档的大小约为 220 字节)。
Codable基本上比 更有限NSCoding。Codable以只能处理具有值语义的对象的方式实现。因此:
这意味着您无法真正围绕/类创建Encoder/包装器(如/ ),而不需要进行大量簿记来检测这些场景和. (这也意味着您不能支持归档/取消归档任何常规对象,而只能支持那些没有别名或循环的对象)。这就是为什么我选择“创建一个独立的存档并将其编码为”方法。DecoderNSCoderNSCoderNSKeyedArchiverNSKeyedUnarchiverfatalErrorNSCodingData
你得到的实际错误是:
-[_SwiftValue encodeWithCoder:]: 无法识别的选择器发送到实例
这是来自这条线:
aCoder.encode(more, forKey: "more")
Run Code Online (Sandbox Code Playgroud)
问题的原因是more(类型Unward)不符合NSCoding. 但是 Swiftstruct不能符合NSCoding. 您需要更改Unward为NSObject除了符合NSCoding. 这些都不会影响符合Codable.
这是您更新的课程:
class Unward: NSObject, Codable, NSCoding {
var id: Int
var job: String
init(id: Int, job: String) {
self.id = id
self.job = job
}
func encode(with aCoder: NSCoder) {
aCoder.encode(id, forKey: "id")
aCoder.encode(job, forKey: "job")
}
required init?(coder aDecoder: NSCoder) {
id = aDecoder.decodeInteger(forKey: "id")
job = aDecoder.decodeObject(forKey: "job") as? String ?? ""
}
}
class Akward: NSObject, Codable, NSCoding {
var name: String
var more: Unward
init(name: String, more: Unward) {
self.name = name
self.more = more
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(more, forKey: "more")
}
required init?(coder aDecoder: NSCoder) {
name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
more = aDecoder.decodeObject(forKey: "more") as? Unward ?? Unward(id: -1, job: "unk")
}
}
Run Code Online (Sandbox Code Playgroud)
和您的测试值:
var upone = Unward(id: 12, job: "testing")
var adone = Akward(name: "Adrian", more: upone)
Run Code Online (Sandbox Code Playgroud)
您现在可以存档和取消存档:
let encodeit = NSKeyedArchiver.archivedData(withRootObject: adone)
let redone = NSKeyedUnarchiver.unarchiveObject(with: encodeit) as! Akward
Run Code Online (Sandbox Code Playgroud)
你可以编码和解码:
let enc = try! JSONEncoder().encode(adone)
let dec = try! JSONDecoder().decode(Akward.self, from: enc)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3372 次 |
| 最近记录: |