【问题标题】:Should I use weak self inside a computed property's closure?我应该在计算属性的闭包中使用弱自我吗?
【发布时间】:2015-10-07 21:12:16
【问题描述】:

我对在闭包中使用 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,它有一个闭包,它会在内部捕获 self。像这样,

extension Foo{

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

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

【问题讨论】:

  • 闭包也是类的引用类型。因此,如果您正在调用捕获的块/闭包(假设来自代码的另一部分而不是来自同一个类),那么这个闭包将对调用它的实例有一个强引用。这就是为什么有时需要[weak self] 以便您的实例可以稍后解除分配的原因。但是通过添加[weak self],您必须确保在执行闭包时您的self 仍然存在。例如:if let weakSelf = self { /* do your work here*/ }.
  • 也许这篇文章会帮助你更好地理解它:sketchytech.blogspot.de/2014/09/… :)
  • “闭包也是类的引用类型。”你有这方面的资料吗?
  • 虽然 Foo 没有对 bar 的引用,因为它是一个计算机属性,你仍然可以将它分配给 Foo 的另一个实例变量,然后你会得到一个引用循环。所以使用[weak self]是有意义的。
  • @newacct - “‘闭包也是类的引用类型。’你有这方面的资料吗?” ... 我知道那是一条旧评论,但是 The Swift Programming Language 中有一段标题为 Closures are Reference Types

标签: swift closures retain-cycle


【解决方案1】:

考虑:

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] 的地方进行成像。如果barcompletionHandler,那说明它很可能用在一些异步场景中,问题是在这个过程中是否需要保留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()

在上面的例子中,我碰巧在imageTask闭包中使用了[weak self],并不是因为我担心任何强引用循环,而仅仅是因为网络任务通常没有业务要求对模型对象进行强引用. (同样,我也不会亲自将网络接口隐藏在模型对象中。)但是在这个例子中,如果你想确保 Person对象在网络请求完成之前一直保留(例如,您可能希望将网络请求的结果保存在某个本地持久存储中)。

所有这些都说了,我很难想象我会使用上述模式的地方。但底线是这里没有强引用循环,所以理论上你可以忽略[weak self] 而无需担心。但在大多数实际场景中,您通常最终会使用[weak self]

【讨论】:

  • 一如既往的好解释。但是还有一件事,如果属性是存储的类型,并且我在这些观察者中使用 self 来调用属性所属类的实例方法,我是否应该在像 didSet 或 willSet 这样的属性观察者的情况下使用 [weak self]。前任。这个gist.github.com/BohdanOrlov/…中的问候属性@
  • 不,属性观察者不需要[weak self] 模式。我也会删除观察者中的 self. 引用,因为这只是不必要的句法噪音。
猜你喜欢
  • 2018-10-11
  • 1970-01-01
  • 2019-05-27
  • 2022-08-16
  • 2015-04-15
  • 1970-01-01
  • 1970-01-01
  • 2011-01-21
  • 1970-01-01
相关资源
最近更新 更多