【问题标题】:Why does defining CodingKeys for SOME properties require an initializer?为什么为某些属性定义 CodingKeys 需要初始化程序?
【发布时间】:2026-01-08 02:05:03
【问题描述】:

编译没有问题:

class User: Codable {
    let name: String
    let email: String
}

但是,如果我们有一个不是由 CodingKey 表示的属性,它需要一个初始化器:

class User: Codable { // Class 'User' has no initializers
    let name: String
    let email: String

    private enum CodingKeys: String, CodingKey {
        case name = "username"
    }
}

为什么它认为缺少合成初始化程序是问题所在,而不是需要 CodingKey 的警告或错误?为什么这会违反 Decodable 的一致性?

编辑:由于有些人似乎很困惑,我不是在问如何解决错误。这是显而易见的。当您没有为所需属性指定 CodingKey 时,我在问 Codable 协议中发生了什么。

【问题讨论】:

  • 您可以简单地为电子邮件分配一个默认值,这样就不需要电子邮件属性的编码键。
  • 你知道问题出在哪里,即使错误信息不是很好。

标签: ios swift codable


【解决方案1】:

具有至少一个没有默认值的属性的类需要一个初始化器来初始化该属性。

class User {
    let name: String
    let email: String
}

“用户”类没有初始化器

当您遵守Codable,或者更具体地说是Decodable,正如您所指出的,该类将获得一个综合初始化器,即User.init(from: Decoder),它可以解决上述问题。这个合成的初始化器将使用以属性命名的编码键。

class User: Decodable { ... }

只要您在 User 中定义了一个名为 CodingKeys 的枚举(确切的名称很重要),Decodable 一致性的综合将要求每个属性都有一个键。换句话说,只要一个键与属性名称不同,您就必须为每个属性定义一个编码键。

以下代码会执行此操作并编译:

class User: Decodable {
    let name: String
    let email: String

    private enum CodingKeys: String, CodingKey {
        case name = "username"
        case email
    }
}

如果不指定email 键,编译器将不知道如何设置该属性,只有开发人员知道如何设置。因此,编译器不会为您合成它,您必须这样做。

可能存在如下所示的自然默认值:

class User: Decodable {
    let name: String
    let email: String

    enum CodingKeys: String, CodingKey {
        case name = "username"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        name = try container.decode(String.self, forKey: .name)

        // Default email:
        email = "\(name)@example.com"
    }
}

【讨论】:

    【解决方案2】:

    Xcode 抛出了由第二个示例引起的一系列错误中的第一个。如果您添加了一个初始化程序,它接下来可能会告诉您一些与您缺少 name 属性的编码键有关的事情。但是,当您不定义 CodingKeys 时,它确实能够为您创建 CodingKeys 以及默认初始化程序。

    【讨论】:

      【解决方案3】:

      即使你不需要 email 的 CodingKey,你仍然需要声明它的大小写,没有任何值,如下所示:

         private enum CodingKeys: String, CodingKey {
           case name = "username"
           case email
         }
      

      【讨论】: