CodingKeys一致性会生成编译器错误

时间:2019-03-29 14:58:33

标签: swift swift4.2

我有一个符合DynamicKey的结构CodingKey。 然后,我决定将KeyedEncodingContainer的现有功能扩展为对[String: Any]进行编码的功能。

所以现在我进入Struct Foo中的一致性部分,但是遇到编译器错误。

有什么主意,为什么编译器从具有一致性的Foo.CodingKeys继承时说CodingKeys不符合DynamicKey

无效代码:

struct DynamicKey: CodingKey, Equatable, ExpressibleByStringLiteral {
    var stringValue: String
    var intValue: Int? { return nil }

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    init?(intValue: Int) {
        return nil
    }

    //MARK:- Equatable Methods
    public static func == (lhs: DynamicKey, rhs: DynamicKey) -> Bool {
        return lhs.stringValue == rhs.stringValue
    }

    //MARK:- ExpressibleByStringLiteral Methods
    public init(stringLiteral value: String) {
        self.stringValue = value
    }

    public init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }

    public init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }
}

extension KeyedEncodingContainer where Key: CodingKey /*where Key == DynamicKey*/ {
    mutating func encodeDynamicValues(_ value: [String: Any], forKey key: Key) throws {
        //Other code here..
    }
}

struct Foo: Encodable {
    var arr: [String: Any]

    public func encode(to encoder: Encoder) throws {

        //Compiler Error: Instance method 'container(keyedBy:)' requires that 'Foo.CodingKeys' conform to 'CodingKey'
        //However, Foo.CodingKeys conforms to `CodingKey` because `DynamicKey` implements the protocol..
        var container = encoder.container(keyedBy: CodingKeys.self)



        try container.encodeDynamicValues(arr, forKey: .arr)
    }

    enum CodingKeys: DynamicKey {
        case arr
    }
}

但是,如果我将DynamicKey更改为一个类,然后使用&运算符使枚举符合,则编译器错误将消失(如果没有&,它将给出同样的错误)。为什么?

工作代码:

final class DynamicKey: CodingKey { //I don't need the equatable and expressible when it's a class so ignore that part.. adding it doesn't change anything..
    var stringValue: String
    var intValue: Int? { return nil }

    init?(stringValue: String) {
        self.stringValue = stringValue
    }

    init?(intValue: Int) {
        return nil
    }
}

extension KeyedEncodingContainer where Key: CodingKey /*where Key == DynamicKey*/ {
    mutating func encodeDynamicValues(_ value: Any, forKey key: Key) throws {
        //Other Code Here..
    }
}

struct Foo: Encodable {
    var arr: [String: Any]

    public func encode(to encoder: Encoder) throws {
        //CodingKeys now conforms to `CodingKey` because I made `DynamicKey` a class and used the `&` `CodingKey`
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encodeDynamicValues(arr, forKey: .arr)
    }

    enum CodingKeys: DynamicKey & CodingKey {
        case arr
    }
}

1 个答案:

答案 0 :(得分:0)

您的第一个示例

第一个示例中的问题仅仅是您对这一行的理解而已:

enum CodingKeys: DynamicKey

您说:

  

有什么想法为什么编译器说Foo.CodingKeys从具有一致性的DynamicKey继承时不符合CodingKeys?

但是Foo.CodingKeys并不是“继承自DynamicKey”。没有什么可以从结构“继承”。这里的表示法与继承无关。您所做的是一个原始值为DynamicKey类型的枚举。 (通常,您将无法做到这一点-原始值类型必须“可由字符串,整数或浮点文字表示”,但可以通过使DynamicKey Equatable和ExpressibleByStringLiteral来解决此问题。)

与您说过的语法相同:

enum MyEnum : Int {
    case myCase // zero
}

(您也为自己的枚举使用了神奇的名称CodingKeys感到困惑。但这可能不直接相关。)


您的第二个示例

然后在第二个示例(其中DynamicKey是一个类)中,您完全相反地感到困惑。在这里,您做了完全不同的事情:

enum CodingKeys: DynamicKey & CodingKey {

在这里,您的枚举 not 没有任何原始值类型;冒号声明协议限制后的内容。但是在这里,您以另一种方式自欺欺人,因为事实上DynamicKey部分是无关紧要的。如果您只是简单地说,代码也可以编译

enum CodingKeys: CodingKey {

在该示例中真正发生的所有事情是,您要求自动合成与CodingKey的一致性,并且已经获得了它。