我们在什么情况下不应该使用[weak self]和[unowned self]?

7
我在StackOverflow上读到了关于在闭包中何时使用[weak self][unowned self]的讨论。但是,是否有任何情况下我们不需要使用任何一个,因为Swift在闭包中显式使用self时不会显示任何错误或警告。
例如,在这里我们应该使用weak还是unowned
UIView.animate(withDuration: 0.3) {
    self.view.alpha = 0.0
}

1
在任何未提及使用它们的情况下? - Dávid Pásztor
1
还要看看 Swift 编程语言指南中关于 闭包的强引用循环部分 Strong Reference Cycles for Closures - Dávid Pásztor
2个回答

7
如果您的闭包可能导致强引用环,则需要使用 [weak self] 或 [unowned self]。如果将闭包赋值给 self 的属性并在闭包内部引用 self 或 self 的属性,闭包就会持有 self,因此与使用普通类一样,强引用的规则同样适用于闭包。对于您的示例,由于您未将闭包分配给类中的变量,所以不需要使用 [weak self] 或 [unowned self],因此不会产生强引用环。有关更多信息,请参见 Swift 编程语言指南中的 Strong Reference Cycles for Closures 部分。下面是来自上述链接的示例,展示了闭包何时会导致强引用环:
class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }

    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }

    deinit {
        print("\(name) is being deinitialized")
    }

}

如果在asHTML的闭包中没有使用[unowned self],则会因为分配给asHTML的闭包而导致强引用循环。将asHTML的实现更改为以下内容可以解决此问题:

lazy var asHTML: () -> String = {
    [unowned self] in
    if let text = self.text {
        return "<\(self.name)>\(text)</\(self.name)>"
    } else {
        return "<\(self.name) />"
    }
}

等等,这两种asHTML var的实现方式有什么区别? - Legonaftik
不小心在正确的实现中漏掉了[unowned self],现在已经修复。 - Dávid Pásztor

2

这有些基于个人观点,我会给出我的意见 :)

通常我会根据同步性来判断。如果闭包是异步的,调用实例在闭包被调用时可能已经不存在了,因此应该使用[weak self]。如果闭包是同步的,则不需要,直接捕获一个强引用即可。

这个方法也可以扩展到那些你能够合理预期实例在被调用时仍然有效的闭包上(例如你的视图动画),但是要注意这样做假设闭包及其使用将保持不变,因此在未来某个时候它可能会被打破。这种方法不太安全,会使未来的维护更加困难/危险。

对于像UIView.animate这样已经建立和可预测的API,我个人倾向于使用强引用以简洁为原则,但这取决于具体使用情况,您需要自行评估。

另外,正如评论中所指出的,函数闭包也是如此。将闭包分配给另一个变量的属性会产生一组不同的问题。

顺便说一下,我采用弱引用闭包调用我的类型中的另一个方法的方法。

thing.doSomethingWithAClosure() { [weak self]
    self?.doSomething()
}

它简化了逻辑,同时强制执行更多的功能/模块化代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接