Swift 可编码初始化

Posted

技术标签:

【中文标题】Swift 可编码初始化【英文标题】:Swift Codable init 【发布时间】:2018-06-25 03:00:26 【问题描述】:

我想在 Swift Coding/Encoding 功能完成对 JSON 的解码后执行一些初始化逻辑。

struct MyStruct: Codable 
    let id: Int 
    var name: String

    init() 
       name = "\(id) \(name)" 
    

但我得到编译器错误:

Return from initializer without initializing all stored properties

这对我来说很清楚,因为 init() 希望我初始化所有属性。但是添加一个包含所有需要的属性的 init() 也不能解决它,因为当 Codable 启动时,这个初始化器不会被调用(!):

init(id: Int, name: String) 
    // This initializer is not called if Decoded from JSON!
    self.id = id 
    self.name = "\(id) \(name)" 

尽管如此 - 有没有办法在解码完成后执行一些初始化逻辑,但无需手动为每个属性进行所有解码?所以不用每次都执行init(from decoder: Decoder)。在这个简短的示例中,我只有两个简单的属性,但生产代码包含数千个。

谢谢。

【问题讨论】:

为什么不将name 作为计算属性? 你的 json 是否包含“name”键? 谢谢,但这只是一个例子。我需要的是一种运行初始化逻辑的方法。 我认为这是不可能的。我要么屈服并实现了init(from:),要么采用了我在JSON解码后手动调用的finializeInit()方法。 @Gereon 你能解释一下你的 finalizeInit 方法吗?我不清楚。谢谢。 【参考方案1】:

您要么免费获得一切但标准化的东西,要么您必须编写自定义初始化程序,例如

struct MyStruct: Codable  

    let id: Int 
    var name: String

    private enum CodingKeys : String, CodingKey  case id, name 

    init(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(Int.self, forKey: .id)
        let decodedName = try container.decode(String.self, forKey: .name)
        name = "\(id) \(decodedName)" 
    

您可以实现init(),但这与解码功能无关,您必须为所有非可选属性分配一个默认值,这就是错误所说的。

【讨论】:

收到CodingKeys 的错误说'CodingKeys' is inaccessible due to 'private' protection level。你是说CodingKey那里吗? 您必须指定 CodingKeys,我的错,请参阅编辑。 哦,是的,经过一番研究,我已经弄清楚了,并且已经实现了枚举 CodingKeys。不过感谢您的解决方案!【参考方案2】:

使用工厂方法,首先使用init(from:),然后调用你的自定义初始化代码

struct Foo: Decodable 
    let name: String
    let id: Int

    var x: String!

    private mutating func finalizeInit() 
        self.x = "\(name) \(id)"
    

    static func createFromJSON(_ data: Data) -> Foo? 
        guard var f = try? JSONDecoder().decode(Foo.self, from: data) else  
            return nil 
        
        f.finalizeInit()
        return f
    


let sampleData = """
     "name": "foo", "id": 42 
    """.data(using: .utf8)!
let f = Foo.createFromJSON(sampleData)

【讨论】:

以上是关于Swift 可编码初始化的主要内容,如果未能解决你的问题,请参考以下文章

可编码的自定义类类型属性不能从 JSON 初始化它自己的属性

Swift 4 可编码领域对象子类

使用可编码和静态变量的 Plist 结构类

如何在可编码结构中使用计算属性(swift)

Swift 可编码解析 keyNotFound

带有可编码数组的 swift 5 抽象网络响应