什么时候我们不应该使用 [weak self] 和 [unowned self]?
When we should NOT use neither [weak self] nor [unowned self]?
我在 Whosebug 上阅读了一些关于何时应该在闭包中使用 [weak self]
或 [unowned self]
的讨论。
但是,是否存在我们不必使用其中一个 和 的情况,因为 Swift 在我们使用时不会显示任何错误或警告只是在闭包中显式使用 self
。
比如这里应该用weak
还是unowned
?
UIView.animate(withDuration: 0.3) {
self.view.alpha = 0.0
}
这有点基于意见,所以我会给出我的意见:)
我通常基于同步性。如果一个闭包是 Async 的,当关闭器被调用时,调用实例可能不再存在,因此应该使用 [weak self]
。如果一个闭包是同步的,那么它是不必要的并且捕获一个强引用就可以了。
这 可以 扩展为还包括闭包,您可以合理地期望您的实例在被调用时保持有效(例如,您的视图动画案例),但是请注意,这会使假设闭包和您对它的使用将保持不变,因此理论上它可能会在将来的某个时候中断。这不太安全,并且使未来的维护更多 difficult/dangerous。
对于像 UIView.animate 这样的既定且可预测的 API,为了简洁起见,我个人倾向于使用 strong self,但这是您需要自己进行的评估,并且这取决于使用情况。
另外如评论中所述,函数闭包也是如此。将闭包分配给另一个变量的 属性 有一组不同的问题。
顺便说一句,我采用了弱引用闭包的方法,只是在我的类型中调用另一个方法,例如
thing.doSomethingWithAClosure() { [weak self]
self?.doSomething()
}
它简化了逻辑,同时还执行了更多 functional/modular 代码。
如果你的闭包会导致强引用循环,你需要使用 [weak self] 或 [unowned self]。
如果您将闭包分配给self的属性并且您引用self 或 self inside 闭包本身的 属性。闭包是引用类型,因此基本上相同的规则适用于强引用,就像您使用普通 classes.
对于您的示例,不需要 [weak self]
或 [unowned self]
,因为您没有将闭包分配给 class 中的变量,[=14] =]引用,所以不会有强引用循环。
有关详细信息,请查看 Swift 编程语言指南的 Strong Reference Cycles for Closures 部分。以下是上述 link 中闭包导致强引用循环的示例:
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) />"
}
}
我在 Whosebug 上阅读了一些关于何时应该在闭包中使用 [weak self]
或 [unowned self]
的讨论。
但是,是否存在我们不必使用其中一个 和 的情况,因为 Swift 在我们使用时不会显示任何错误或警告只是在闭包中显式使用 self
。
比如这里应该用weak
还是unowned
?
UIView.animate(withDuration: 0.3) {
self.view.alpha = 0.0
}
这有点基于意见,所以我会给出我的意见:)
我通常基于同步性。如果一个闭包是 Async 的,当关闭器被调用时,调用实例可能不再存在,因此应该使用 [weak self]
。如果一个闭包是同步的,那么它是不必要的并且捕获一个强引用就可以了。
这 可以 扩展为还包括闭包,您可以合理地期望您的实例在被调用时保持有效(例如,您的视图动画案例),但是请注意,这会使假设闭包和您对它的使用将保持不变,因此理论上它可能会在将来的某个时候中断。这不太安全,并且使未来的维护更多 difficult/dangerous。
对于像 UIView.animate 这样的既定且可预测的 API,为了简洁起见,我个人倾向于使用 strong self,但这是您需要自己进行的评估,并且这取决于使用情况。
另外如评论中所述,函数闭包也是如此。将闭包分配给另一个变量的 属性 有一组不同的问题。
顺便说一句,我采用了弱引用闭包的方法,只是在我的类型中调用另一个方法,例如
thing.doSomethingWithAClosure() { [weak self]
self?.doSomething()
}
它简化了逻辑,同时还执行了更多 functional/modular 代码。
如果你的闭包会导致强引用循环,你需要使用 [weak self] 或 [unowned self]。
如果您将闭包分配给self的属性并且您引用self 或 self inside 闭包本身的 属性。闭包是引用类型,因此基本上相同的规则适用于强引用,就像您使用普通 classes.
对于您的示例,不需要 [weak self]
或 [unowned self]
,因为您没有将闭包分配给 class 中的变量,[=14] =]引用,所以不会有强引用循环。
有关详细信息,请查看 Swift 编程语言指南的 Strong Reference Cycles for Closures 部分。以下是上述 link 中闭包导致强引用循环的示例:
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) />"
}
}