我应该在计算 属性 的闭包中使用 weak self 吗?
Should I use weak self inside a computed property's closure?
我对在闭包中使用 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]
吗?
考虑:
extension Foo {
var bar: Bar {
let bar = Bar()
bar.completionHandler = {
print(self.x)
}
return bar
}
}
Should I use [weak self]
inside this closure?
通常当人们问这个问题时,真正关心的是“我需要 [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()
在上面的例子中,我恰好在imageTask
闭包中使用了[weak self]
,并不是因为我担心任何强引用循环,而是因为网络任务通常没有业务声明对模型对象的强引用。 (然后,我也不会亲自将网络接口埋在模型对象中。)但是在这个例子中,可以省略这个闭包的 [weak self]
引用,例如,如果你想确保 Person
对象一直保留到网络请求完成(例如,也许您想将网络请求的结果保存在某个本地持久存储中)。
说了这么多,我真的很难想象我会在哪里使用上述模式。但最重要的是这里没有强引用循环,所以理论上你可以忽略 [weak self]
而不必担心。但在大多数实际情况下,您通常最终会使用 [weak self]
.
我对在闭包中使用 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]
吗?
考虑:
extension Foo {
var bar: Bar {
let bar = Bar()
bar.completionHandler = {
print(self.x)
}
return bar
}
}
Should I use
[weak self]
inside this closure?
通常当人们问这个问题时,真正关心的是“我需要 [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()
在上面的例子中,我恰好在imageTask
闭包中使用了[weak self]
,并不是因为我担心任何强引用循环,而是因为网络任务通常没有业务声明对模型对象的强引用。 (然后,我也不会亲自将网络接口埋在模型对象中。)但是在这个例子中,可以省略这个闭包的 [weak self]
引用,例如,如果你想确保 Person
对象一直保留到网络请求完成(例如,也许您想将网络请求的结果保存在某个本地持久存储中)。
说了这么多,我真的很难想象我会在哪里使用上述模式。但最重要的是这里没有强引用循环,所以理论上你可以忽略 [weak self]
而不必担心。但在大多数实际情况下,您通常最终会使用 [weak self]
.