NSCoding 和 Codable 可以共存吗?

2023-12-28

在测试新的 Codable 如何与 NSCoding 交互时,我整理了一个游乐场测试,涉及使用包含 Codable 结构的类的 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)

上述内容已被 Playground 接受,不会产生任何编译器错误。

但是,如果我尝试“保存完毕”,如下所示:

let encodeit = NSKeyedArchiver.archivedData(withRootObject: adone)

游乐场立即崩溃并出现以下错误:

错误:执行被中断,原因:EXC_BAD_INSTRUCTION(代码=EXC_I386_INVOP,子代码=0x0)。

为什么? 有没有办法让 NSCoding 类包含 Codable 结构?


现有的答案并没有真正解决互操作的问题,而是展示了如何从NSCoding to Codable.

我有一个用例,这不是一个选项,而且我确实需要使用NSCoding from a 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
    }
}

这是我为其编写的简单测试:

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
            }
        }
    }
}

一些注意事项:

  1. 如果您需要走另一条路(嵌入Codable里面的物体NSCoding存档),您可以使用添加到的现有方法NSCoder/NSDecoder

  2. 这将为每个对象创建一个新的存档。除了在编码/解码期间添加相当多的对象分配之外,它还可能使结果膨胀(在我的测试中,空存档的大小约为 220 字节)。

  3. Codable基本上比NSCoding. Codable以只能处理具有值语义的对象的方式实现。因此:

    • 具有别名(对同一对象的多个引用)的对象图将导致对象重复
    • 带有循环的对象图永远无法解码(会有无限递归)

    这意味着你无法真正制作一个Encoder/Decoder包装纸NSCoder/NSCoder类(如NSKeyedArchiver/NSKeyedUnarchiver),无需进行大量簿记来检测这些场景并且fatalError。 (这也意味着您不能支持存档/取消存档任何常规NSCoding对象,但仅限那些没有别名或循环的对象)。这就是为什么我选择“制作一个独立的存档并将其编码为Data“ 方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NSCoding 和 Codable 可以共存吗? 的相关文章

随机推荐

  • 仅在 Vim 中启用 .h 和 .cpp 文件的某些插件和选项

    我在 Vim 中安装了 delimitMate 以完成大括号 但它针对所有文件运行 而不仅仅是 h 和 cpp 文件 DelimitMate 有一个在缓冲区中禁用自身的选项 因此我需要在 vimrc 中添加一些内容 表示 在除 h 和 cp
  • 从应用程序脚本中的电子表格更新下拉列表

    我正在尝试学习 Google 的 HTML Service UI 服务 并且正在努力弄清楚如何根据电子表格中的数据更新 UI 中的下拉列表 我从以下位置复制了以下代码这个谷歌教程 https developers google com ap
  • 授权 Google Drive Android API

    我尝试通过以下方式访问 Google 云端硬盘中的数据谷歌云端硬盘 Android API https developers google com drive android auth 不是 Web API 令人疯狂的是 当我使用此访问权限
  • Windows 上的 script/generate:“script”未被识别为内部或外部命令

    每当我尝试使用 Rails 时script generate or script install命令我收到这种错误 C workspace gt script generate bigcommand script is not recogn
  • 如何在 MySQL 中创建表别名

    我正在将 MS Access 应用程序 已将表链接到 MSSQL Server 迁移到 MySQL 作为克服一些 MSAccess 表命名问题的方法 我正在寻求一种解决方案来添加 MySQL 表别名 该别名将指向 MySQL 数据库中的现有
  • Javascript 隐藏所选选项

    我有这段代码来隐藏选定的选项 function connect selectbox option show selectbox each function i var obj selectbox option value this val
  • Django自定义用户管理员change_password

    我成功地在 django 中使用了自定义用户模型 最后要做的事情是超级用户更改任何用户密码的 AdminChangePasswordForm 目前 来自 admin myapp user 的更改密码链接给出了 404 答案 覆盖 get u
  • 数组的长度属性在哪里定义?

    我们可以确定一个的长度ArrayList
  • 如何在 C# 中将 SID 转换为帐户名

    我有一个 C 应用程序 可以扫描目录并收集一些信息 我想显示每个文件的帐户名 我可以通过获取 FileInfo 对象的 SID 在本地系统上执行此操作 然后执行以下操作 string GetNameFromSID SecurityIdent
  • C++ 相当于 Python __getattr__(self, name)

    我喜欢 Python 的原因之一是它的方式自定义属性访问 https docs python org 2 reference datamodel html customizing attribute access class Foo obj
  • 在Prolog中查找最大子列表

    我是 Prolog 新手 正在尝试解决以下问题的实例最大子数组问题 https en wikipedia org wiki Maximum subarray problem 我有以下相当优雅的 C 代码 int maxSubArray ve
  • 带阴影的 UIView

    我试图在一个简单的 UIView 对象周围创建一个阴影 该对象添加到 UIViewController 视图的顶部 这样做最直接的方法是什么 首先 请务必导入 Quartz Core 库 import
  • Foundation 可以告诉我 Objective-C 方法是否需要特殊的结构返回吗?

    据我了解 背景 Objective C 方法调用基本上是带有两个隐藏参数 接收器和选择器 的 C 函数调用 Objective C 运行时包含一个名为 objc msgSend 的函数 允许以这种方式调用方法 不幸的是 当函数返回结构时 可
  • 为 OpenCL 设置 Visual Studio 2010 的正确方法

    设置 VisualStuio 2010 处理 cl 文件的正确方法是什么 我在工具 文本编辑器 文件扩展名下添加了 cl 并将 usertype dat 复制到 common7 ide 文件夹中 但 VS 为 float4 或 cross
  • 如何在登录后正确注销 Java EE 6 Web 应用程序

    一个非常简单的要求 登录 Web J2EE 6 应用程序后 如何让用户再次注销 我见过的大多数 全部 书籍和教程都展示了如何向其应用程序添加登录 登录错误页面 并使用 j security check 方法演示安全主体 角色 领域等的使用
  • 处理 SQL 连接

    我有一个连接到数据库并检索数据表的 SQL 类 我知道 SqlConnection 必须在完成后释放 我知道这可以使用using块 但是也可以接受Dispose 在这个类的析构函数中调用 这是我的代码 public class SQLEng
  • 使用 Visual Studio 2010 时出现 System.OutOfMemoryException

    当我的笔记本电脑中确实有很多未使用的内存时 我收到了名为 system outofmemory exception 的非常烦人的消息 除非我关闭并重新打开解决方案 否则它不会让我继续我的工作 是否有任何配置或服务包可以解决该问题 Thank
  • 编辑后保留文本的突出显示颜色

    删除前面一行的内容后 无法保留我在 RichTextBox 中设置的文本上的突出显示效果 无论我从控件中删除多少文本 它总是会删除我设置为已包含在其中的文本的自定义选择颜色和选择背景颜色 我的删除方法的代码 private void btn
  • asdf erlang 在 macOS 上编译失败

    我正在尝试通过 asdf 在 macOS 11 3 1 上安装 erlang 22 3 4 18 erlang 的 asdf 插件 https github com asdf vm asdf erlang OSX 特定的说明说要安装auto
  • NSCoding 和 Codable 可以共存吗?

    在测试新的 Codable 如何与 NSCoding 交互时 我整理了一个游乐场测试 涉及使用包含 Codable 结构的类的 NSCoding 到惠特 struct Unward Codable var id Int var job St