在方便的初始值设定项中使用 self = 来委托给 JSONDecoder 或 Swift 中的工厂方法,以避免“无法分配给值:'self' 是不可变的”

2024-03-28

有时在 Swift 中,为委托的类编写一个初始化程序可能会很方便JSONDecoder或工厂方法。例如,有人可能想写

final class Test: Codable {
    let foo: Int
    
    init(foo: Int) {
        self.foo = foo
    }
    
    func jsonData() throws -> Data {
        try JSONEncoder().encode(self)
    }
    
    convenience init(fromJSON data: Data) throws {
        self = try JSONDecoder().decode(Self.self, from: data)
    }
}

let test = Test(foo: 42)
let data = try test.jsonData()
let decodedTest = try Test(fromJSON: data)
print(decodedTest.foo)

但这无法编译

Cannot assign to value: 'self' is immutable.

解决这个问题的解决方案是什么?


首先,请注意,此限制仅存在于classes,因此示例初始化程序将按原样工作structs and enums,但并非所有情况都允许更改class到这些类型之一。

此限制对class初始化器是一个常见的痛点,经常出现在这个网站上(例如 https://stackoverflow.com/questions/65615354/swift-how-to-parse-data-to-class-in-extension-convenience-init)。有一个thread https://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924/16在 Swift 论坛上讨论这个问题,并且已经开始添加必要的语言功能来使上面的示例代码编译,但这在 Swift 5.4 中尚未完成。 从线程:

Swift 自己的标准库和 Foundation 通过使类符合虚拟协议并在必要时使用协议扩展初始值设定项来解决此缺失的功能。

使用这个想法来修复示例代码的结果

final class Test: Codable {
    let foo: Int
    
    init(foo: Int) {
        self.foo = foo
    }
    
    func jsonData() throws -> Data {
        try JSONEncoder().encode(self)
    }
}

protocol TestProtocol: Decodable {}
extension Test: TestProtocol {}
extension TestProtocol {
    init(fromJSON data: Data) throws {
        self = try JSONDecoder().decode(Self.self, from: data)
    }
}

let test = Test(foo: 42)
let data = try test.jsonData()
let decodedTest = try Test(fromJSON: data)
print(decodedTest.foo)

效果很好。如果Test是唯一符合的类型TestProtocol,那么只有Test将得到这个初始化器。

另一种选择是简单地扩展Decodable或您的类符合的其他协议,但如果您不希望符合该协议的其他类型也获得您的初始值设定项,则这可能是不可取的。

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

在方便的初始值设定项中使用 self = 来委托给 JSONDecoder 或 Swift 中的工厂方法,以避免“无法分配给值:'self' 是不可变的” 的相关文章

随机推荐

  • c# Xamarin UITextField 设置光标位置

    我需要将 UITextField 的光标定位在与另一个先前文本字段完全相同的位置 我将光标在第一个文本字段中的位置设为nint index txtToField GetOffsetFromPosition txtToField Beginn
  • Android 时差与乔达时间

    我有这个简单的代码 使用 Joda time 工作正常 但我有一个问题 比如返回814分钟 通过代码就可以了 但我希望结果少于 60 分钟 而不是 814 分钟 那么 我该如何转换这 814 分钟才能得到我想要的结果呢 同样的情况也发生在几
  • YouTube Live API 流状态和质量回调

    In the Live Control Room of a YouTube Live broadcast I can see a Stream Status view which shows me details of the video
  • 尝试上传到 Amazon S3 时出现“不支持的正文有效负载对象”

    我想将文件从我的前端上传到我的 Amazon S3 AWS 我正在使用 dropzone 因此我转换文件并将其发送到我的后端 在我的后端我的文件是这样的 fieldname file originalname test torrent en
  • 从 Mono 列表创建 Flux 的正确方法

    假设我有一个使用 CustomObjects 列表的 API 操作 对于其中的每一个对象 它都会调用一个创建 Mono 的服务方法 如何以惯用且非阻塞的方式从这些 Mono 对象创建 Flux 我现在想到的就是这个 我更改了方法名称以更好地
  • 如何在 Telegram API 中转发消息

    Telegram API 中有 2 种转发消息的方法 messages forwardMessage messages forwardMessages 我想用forwardMessage转发消息的方法channel group or use
  • 修复了 div 内的非滚动页脚?

    我正在页面中央制作一个小 div 它有一个固定的页脚 但该 div 是可滚动的 根据我的说法 有两种方法可以做到这一点 Using position fixed 固定位置实际上是相对于浏览器窗口起作用的 但我想要相对于我的小 div 的位置
  • 如何测试一个字符串是否包含多个子字符串之一?

    我想知道一个字符串是否包含以下之一abc def xyz等等 我可以这样做 a Contains abc or a Contains def or a Contains xyz 好吧 它可以工作 但是如果这个子字符串列表发生变化 我必须更改
  • 如何在 mvc3 razor 视图中使用会话变量保留数据?

    我正在使用 Razor 开发 MVC3 应用程序 在验证用户后 在我的帐户控制器中 我从数据库获取用户 ClientID 这里我想将 ClientID 保留在 Session 变量中 它在所有控制器和 Razor 视图中使用 我不知道实现此
  • 在Java中删除文件的内容

    我正在将字节写入 temp fls 文件 完成操作后 我想从 temp fls 文件中删除最后 256 个字节 我怎样才能实现这个目标 请帮我 提前致谢 Use RandomAccessFile setLength http java su
  • 如何在 Ansible 中的 do-until 循环中指定多个条件

    我正在拨打 REST 电话 并想在继续之前检查我的请求是否已完成 在响应中 我收到 PENDING 或 IN PROGRESS 作为 request status 我想等到我得到 完成 或 失败 为此 我想在收到 PENDING 或 IN
  • 如何分析由忽略的ExceptionInInitializerError引起的NoClassDefFoundError?

    今天我花了一个下午的时间来分析 NoClassDefFoundError 一次又一次验证类路径后 发现有一个类的静态成员抛出了第一次被忽略的异常 之后 每次使用该类都会抛出 NoClassDefFoundError 而没有有意义的堆栈跟踪
  • Azure Blob 存储在 Microsoft 中时是否已加密?

    我正在开发一个在 Azure Blob 存储中存储文本的网站 文本可能很敏感 不一定是密码 而是个人信息 我正在尝试决定是否应该在将文本存储到 Azure Blob 存储之前对其进行加密 我的理解是 如果 Azure 密钥和帐户名泄露并且恶
  • 如何通过反射引用字段

    抱歉 标题不明确 进一步回答我之前的问题 https stackoverflow com questions 4315905 how to pass parameters to a method by reflection 我想订阅动态检索
  • src 中访问不同目录中文件的路径

    我有一个名为projects其中我有一个game文件夹和一个engine文件夹 我有我的engine js文件内的engine文件夹 我想知道我是否可以用我的game html像这样从其他文件夹中获取文件 现在 显然上面的方法不起作用 但我
  • 如何从 JQuery 对象获取 javascript 控件?

    我是 JQuery 的初学者 如何从 JQuery 对象获取作为 javascript 对象的控件 var object this 最常见的var object this 0 如果匹配的元素超过 1 个 this 0 this 1 this
  • 无法将 .war 应用程序部署到 GlassFish 3.1.2

    我有一个 war 应用程序模块 无需任何异常更改和服务器调整即可成功部署 但是 我无法将此应用程序部署到 GF 3 1 2 服务器抛出以下异常 java lang Exception java lang IllegalStateExcept
  • 小数的小数次方?

    net 框架在 Math 类中提供了一种对 double 进行幂运算的方法 但根据精度要求 我需要将小数提高到小数次方 Pow decimal a decimal b 框架有这样的功能吗 有谁知道有这种功能的库吗 为了解决我的问题我找到了一
  • 使用 aws cli 获取 S3 存储桶的 ARN

    是否可以通过AWS命令行获取S3存储桶的ARN 我已经查看了文档aws s3api and aws s3 并且还没有找到一种方法来做到这一点 总是如此arn PARTITION s3 NAME OF YOUR BUCKET 如果您知道存储桶
  • 在方便的初始值设定项中使用 self = 来委托给 JSONDecoder 或 Swift 中的工厂方法,以避免“无法分配给值:'self' 是不可变的”

    有时在 Swift 中 为委托的类编写一个初始化程序可能会很方便JSONDecoder或工厂方法 例如 有人可能想写 final class Test Codable let foo Int init foo Int self foo fo