1

我有一些对象,它们是我通过协议扩展提供的函数structs从 JSON 字典 ( [String : Any])初始化的(请参阅使用字典/数组初始化符合 Codable 的对象)。initDecodable

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

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更改时设置它。

我可以使用didSeton 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 initDecodable闭包中的那个:),try? ObjectModelB(any: $0)因为我的对象是 astruct并且这不是继承,它只是协议提供的初始化程序。否则,我会“覆盖”init我的对象中的 ,然后只是执行super.init某种变异函数来更新我的复杂属性,然后我会创建我的复杂属性private(set)

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

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

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

4

1 回答 1

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

现在,如果我定义一个与扩展中提供的函数签名相同的initon ,如下所示:ObjectModelBinitDecodable

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工厂方法使用initfrom创建我的类型的实例Decodable,然后再做我想做的任何其他事情。这样,我可以控制哪些对象需要特殊的初始化处理,哪些不需要,但是在创建对象时,没有任何变化。您仍然使用相同的init(any:)功能。

于 2018-06-13T23:02:26.293 回答