mardi 14 novembre 2017

Swift - Avoiding strong reference cycles in closure-based configuration

Please note: I have read many of the (very many) answers and articles on the subject of avoiding strong reference cycles. However, I'm looking for guidance on how to handle a specific by-product of avoiding these cycles.

In the following example, the class Foo is designed to be configured using a closure. A reference to the closure is cached for later use.

The closure will be called whenever the Model data is needed. In order for Foo to work properly, the data must exist.

class Foo
{
    typealias ModelGetter = (() -> Model)
    fileprivate var _modelGetter: ModelGetter!

    ...

    func configure(with modelGetter: @escaping ModalGetter)
    {
        _modelGetter = modelGetter
    }

    func printLastestModel()
    {
        // Get the latest model, do something with it.
        precondition(_modelGetter != nil)
        let model = _modelGetter()
        print(model)
    }
}

In the above code, _modelGetter is implicitly unwrapped. Although I could define it as an Optional, and unwrap it as needed, Foo always needs the closure to be set in order to work properly, hence the implicit unwrapping.

Making an instance of Foo, and configuring it:

let foo = Foo()
foo.configure(with: { self.makeModel() })
foo.printLatestModel()

But, this creates a retain cycle.

So, [weak self] is used, and we check for self's optionality:

foo.configure(with: { [weak self] in
    guard let strongSelf = self else { return **WHAT** }
    return strongSelf.makeModel()
})

Problem

This requires that even though self may be nil, the closure still needs to return a Model (i.e. WHAT?) to the caller of the closure (the Foo instance.) But, since self is nil, I don't have a Model to hand over.

Question

Can someone recommend a pattern to use in this situation? Preferably, I don't want Foo to be checking if the modelGetter is valid or not, or left wondering if the Model is valid. For Foo's purposes, if Foo exists, then it must always be able to get the Model it needs.

Or, should I just redesign Foo's needs, to take in to account the possibility of not being able to procure a Model? Thankyou for any help.

Aucun commentaire:

Enregistrer un commentaire