在Swift中,如何在创建对象后立即调用函数

时间:2018-06-13 21:53:22

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

我有一些structs的对象,我通过[String : Any]协议上的扩展提供的init函数从JSON字典(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更改,它还有一个我真正想要设置的属性。

我可以在didSet上使用arrayOfObjectModelAVal,但这只会捕获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

我也无法“覆盖”init Decodable协议提供的init(闭包中的那个:try? ObjectModelB(any: $0)),因为我的对象是{{1}这不是继承,它只是协议提供的初始化程序。否则,我会“覆盖”我的对象中的struct,然后执行init,然后执行一些更新我复杂属性的变异函数,然后创建复杂的属性{{1} }。

我能想到的唯一另一个选择是创建我刚刚提到的变异函数,并在super.init更改private(set)时调用它,然后使用类似这样的内容更新我的对象初始化调用:

didSet

但现在任何人都可以随时调用arrayOfObjectModelAVal(这很糟糕,因为它真的很费力),并且无法保证在创建对象数组时它甚至被调用,因为开发人员可能会忘记做guard let objectDictArray = responseObject as? [[String : Any]] else { return } let objects = objectDictArray .compactMap({ try? ObjectModelB(any: $0) }) .forEach({ $0.updateProperties() }) 部分。因此,为什么我想要一种在对象初始化后立即自动调用updateProperties函数的方法。

1 个答案:

答案 0 :(得分:0)

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

extension Decodable {
    init(any: Any) throws {
        // https://stackoverflow.com/questions/46327302
    }

    static func createFrom(any: Any) throws -> Self {
        return try Self.init(any: any)
    }
}

现在,如果我在init上定义一个ObjectModelB,其功能签名与init扩展程序中提供的Decodable相同,就像这样:

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()
    }
}

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