我应该在计算属性的闭包中使用弱自我吗?

时间:2015-07-18 04:34:38

标签: swift closures retain-cycle

我对在闭包中使用self感到困惑。

我们什么时候应该宣布[weak self]?我理解的一个明显的例子是

class Foo{

    var closure: ( Void -> Void )?
    var x = 0

    func doSomething(){
        closure = { [weak self] in
            if let x = self?.x{
                println(x)
            }
        }
    }
}

但是,如果我想创建一个计算属性bar,它有一个闭包,它会捕获内部的自我。像这样,

extension Foo{

    var bar: Bar{
        let bar = Bar()
        bar.completionHandler = {
            println(self.x)
        }
        return bar
    }
}

我应该在这个闭包中使用[weak self]吗?

1 个答案:

答案 0 :(得分:0)

考虑:

extension Foo {
    var bar: Bar {
        let bar = Bar()
        bar.completionHandler = {
            print(self.x)
        }
        return bar
    }
}
  

我应该在此闭包内使用[weak self]吗?

通常,当人们问这个问题时,真正关心的是“我需要[weak self]来避免强参考周期吗?”的答案是,不,这里没有强参考周期。

如果bar是存储属性,则缺少[weak self]会引发危险信号。如果我们有一个Foo存储了对Bar的引用,而这个引用本身又有一个闭包,其中的闭包中有一个self引用返回到原始Foo,这很容易出现问题。但是使用这种计算的属性,Foo并没有对bar的任何强烈引用,因此大大减少了对强引用周期的关注。

话虽如此,但我很难想象我不想使用[weak self]。如果bar有一个completionHandler,则表明它很可能在某些异步情况下使用,问题是在此过程中是否需要保留Foo

因此,您真正应该使用[weak self]的问题归结为Bar是什么,以及它是否对Foo拥有合理的所有权要求。

让我们尝试提出一个实际的例子。 (以下内容有些人为,因为我很难想象这种模式的良好用例,但请耐心等待。)

例如,假设Foo是一个Person对象,而Bar是某个URLSessionTask,用于图像下载任务:

class Person {
    let name: String
    private(set) var image: UIImage?

    ...
}

extension Person {
    var imageTask: URLSessionTask {
        let url = ...
        return session.dataTask(with: url) { [weak self] data, _, _ in
            guard let data = data, let image = UIImage(data: data) else { return }

            self?.image = image
        }
    }
}

所以控制器可能会说

let person = Person(...)
let task = person.imageTask
task.resume()

在上面的示例中,我碰巧在[weak self]闭包中使用了imageTask,不是因为我担心任何强引用周期,而是因为网络任务通常没有业务声称强引用在模型对象上。 (然后,同样,我也不会亲自将网络接口掩埋在模型对象中。)但是在此示例中,例如,如果您要确保{ {1}}对象会一直保留到完成网络请求为止(例如,您可能希望将网络请求的结果保存在某些本地持久性存储中)。

所有这些,我很难想象要完全使用上述模式。但最重要的是这里没有强大的参考周期,因此从理论上讲,您可以忽略[weak self]而不必担心。但是在大多数实际情况下,您通常最终会使用Person