Swift - 在基于闭包的配置中避免强大的参考周期

时间:2017-11-15 02:19:50

标签: swift design-patterns memory-management

请注意:我已经阅读了许多关于避免强参考周期的(非常多)答案和文章。但是,我正在寻找有关如何处理避免这些循环的特定副产品的指导。

在以下示例中,类Foo旨在使用闭包进行配置。缓存对闭包的引用以供以后使用。

只要需要Model数据,就会调用闭包。为了使Foo正常工作,数据必须存在。

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

在上面的代码中,_modelGetter被隐式解包。虽然我可以将它定义为Optional,并根据需要解开它,但Foo总是需要设置闭包才能正常工作,因此隐式解包。

制作Foo的实例并进行配置:

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

但是,这会创建一个保留周期。

因此,[weak self]被使用,我们会检查self的可选性:

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

问题

这要求即使self可能为nil,闭包仍然需要将一个Model(即WHAT?)返回给闭包的调用者(Foo实例)。但是,由于self是nil,我不会'有一个模型可以移交。

问题

有人可以推荐在这种情况下使用的模式吗?最好,我不希望Foo检查modelGetter是否有效,或者想知道模型是否有效。出于Foo的目的,如果Foo存在,那么它必须始终能够获得它所需的模型。

或者,我是否应该重新设计Foo的需求,以考虑无法获得模型的可能性?谢谢你的帮助。

2 个答案:

答案 0 :(得分:1)

如果Class是Foo的所有者,建议您设置[unowned self]而不是[weak self]。它将解决问题。如果一个类是另一个类的所有者,它将永远不会出现问题,但如果逻辑错误,它将显示崩溃并不坏,因为它向您发出信号,表明您在项目中损坏了某些内容。

答案 1 :(得分:1)

  

但是,这会产生一个保留周期。

不,它没有。 foo拥有对闭包的引用,而闭包拥有对self的引用(显然不是foo)。如果您在Foo中拥有self类型的强大属性并且将其设置为foo,则只会获得保留周期。

假设您要这样做,我会使用以下两种模式之一:

  • 我作为程序员假设self将在foo的生命周期中始终存在。在这种情况下,我会使用[unowned self]而不是[weak self]。如果我的假设被证明是错误的,程序将中止,我将能够根据堆栈跟踪修复错误。显然,您需要一个相当广泛的测试套件来尽可能验证该假设。

  • 我认为self消失是有效的,在这种情况下你有三个选择来处理消失:

    • 使闭包的返回类型可选,即() -> Model?并使Foo能够处理nil模型
    • 如果Modelself,则返回默认nil
    • 如果selfnil,请让闭包声明它会抛出并抛出错误。

我想我可能会选择[unowned self],并确保我明确地在某处明确地提及它。