【问题标题】:In Swift, how can you call a function immediately after object creation在 Swift 中,如何在对象创建后立即调用函数
【发布时间】:2018-11-23 14:26:55
【问题描述】:

我有一些对象,它们是 structs,我通过 init 函数从 JSON 字典 ([String : Any]) 初始化,该函数由 Decodable 协议上的扩展提供(参见 Init an object conforming to Codable with a dictionary/array)。

所以基本上,我的对象看起来像这样:

struct ObjectModelA: Codable {
    var stringVal: String
    var intVal: Int
    var boolVal: Bool

    // Coding keys omitted for brevity
}

struct ObjectModelB: Codable {
    var doubleVal: Double
    var arrayOfObjectModelAVal: [ObjectModelA]

    // Coding keys omitted for brevity

    var complicatedComputedVar: String {
        // expensive operations using the values in arrayOfObjectModelAVal
    }
}

ObjectModelB 包含一个ObjectModelA 数组,它还有一个属性,我只想在arrayOfObjectModelAVal 更改时才设置它。

我可以在arrayOfObjectModelAVal 上使用didSet,但这只会捕获未来对arrayOfObjectModelAVal 属性的更改。问题是我正在使用 Web 服务来检索 JSON 数据以创建 ObjectModelB ([[String : Any]]) 的数组,并且我是这样构建它的:

guard let objectDictArray = responseObject as? [[String : Any]] else { return }
let objects = objectDictArray.compactMap({ try? ObjectModelB(any: $0) })

我的对象在 compactMap 闭包内创建,init 不会触发 didSet

我也不能从Decodable 协议(闭包中的那个:try? ObjectModelB(any: $0))“覆盖”由init 提供的初始化,因为我的对象是struct,这不是继承,它只是一个协议提供的初始化器。否则,我会在我的对象中“覆盖”init,然后只执行super.init,然后执行某种变异函数来更新我的复杂属性,然后将我的复杂属性设为private(set)

我能想到的唯一其他选择是创建我刚才提到的变异函数,并在 arrayOfObjectModelAVal 更改时在 didSet 中调用它,然后使用以下内容更新我的对象初始化调用:

guard let objectDictArray = responseObject as? [[String : Any]] else { return }
let objects = objectDictArray
    .compactMap({ try? ObjectModelB(any: $0) })
    .forEach({ $0.updateProperties() })

但是现在updateProperties 可以随时被任何人调用(这很糟糕,因为它真的很费力),并且无法保证在创建对象数组时它甚至会被调用,因为开发人员可能会忘记执行 @ 987654349@ 部分。因此,为什么我想要一种在对象初始化后立即自动调用 updateProperties 函数的方法。

【问题讨论】:

    标签: swift swift4 swift-protocols decodable swift-structs


    【解决方案1】:

    我想出了一种使用工厂方法来完成此任务的方法。就像我在原始问题中所说的那样,我要使用的初始化程序由Decodable 上的协议扩展提供(请参阅Init an object conforming to Codable with a dictionary/array)。我继续在Decodable 扩展中添加了一个createFrom 静态函数,如下所示:

    extension Decodable {
        init(any: Any) throws {
            // https://stackoverflow.com/questions/46327302
        }
    
        static func createFrom(any: Any) throws -> Self {
            return try Self.init(any: any)
        }
    }
    

    现在,如果我在 ObjectModelB 上定义一个 init,其函数签名与 Decodable 扩展中提供的 init 相同,如下所示:

    struct ObjectModelB: Codable {
        var doubleVal: Double {
            didSet {
                computeComplicatedVar()
            }
        }
        var arrayOfObjectModelAVal: [ObjectModelA] {
            didSet {
                computeComplicatedVar()
            }
        }
    
        // Coding keys omitted for brevity
    
        private(set) var complicatedVar: String = ""
    
        mutating private func computeComplicatedVar() {
            // complicated stuff here
        }
    
        init() {
            doubleVal = 0.0
            arrayOfObjectModelAVal = []
        }
    
        init(any: Any) throws {
            self.init()
            self = try ObjectModelB.createFrom(any: any)
            computeComplicatedVar()
        }
    }
    

    这似乎有效。我喜欢它,因为如果我不添加与Decodable 扩展中提供的完全匹配的init,那么我的对象仍然可以使用Decodable 扩展中提供的那个。但如果我确实提供了自己的,我只需使用createFrom 工厂方法使用来自Decodableinit 创建我的类型的实例,然后再做我想做的任何其他事情。这样,我可以控制哪些对象需要特殊的初始化处理,哪些不需要,但是在创建对象时,没有任何变化。你仍然使用相同的init(any:) 函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      • 2012-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多