Swift 3: 在@escaping 闭包中捕获强自体,无需异步工作
Swift 3: capture strong self in @escaping closure without asynchronous work
有一个声明如下的协议:
typealias SuggestionSourceCallback = ([Suggestion]) -> ()
protocol SuggestionSource {
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback)
}
两个 classes 实现了这个协议。首先 class 异步获取建议(通过 GCD)
final class FisrtClass: SuggestionSource {
private let queue = DispatchQueue(label: "my.app.queue", attributes: [])
private var lastQuery: SuggestionQuery?
// ...
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback) {
self.queue.async { [weak self] in
// capturing strong self
guard let strongSelf = self else {
return
}
// referencing self here, for example
guard self.lastQuery == query else {
return
}
// suggestions is a local variable
var suggestions: [Suggestion] = []
// ...
DispatchQueue.main.async {
callback(suggestions)
}
}
}
}
...而第二个 class 同步执行
final class SecondClass: SuggestionSource {
// ...
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback) {
// ...
callback(self.suggestions[query])
}
}
我的问题是:
- 我应该在
FirstClass
的实现中捕获 strongSelf
吗?
- 我应该在
SecondsClass
的实现中捕获 strongSelf
吗?
更新
补充问题。假设 SecondClass
的 suggestions
作为 static let
,在这种情况下会是什么模式?
final class SecondClass: SuggestionSource {
static let suggestions: [String: [SuggestionQuery]] = {
// ...
}()
// ...
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback) {
// ...
callback(self.suggestions[query])
}
}
在SecondClass
中,不需要创建strongSelf
变量。你会把它放在哪里?关键是 self
保证不会 nil
无论如何,因为你 运行 在其方法之一的范围内。
你的附加问题也是如此,只是出于不同的原因。 suggestions
现在是 static
,所以用 self
作为前缀是一个语法问题,(我假设你也想用 static
作为 suggest
方法的前缀) .
然而,在 FirstClass
中,捕获 strongSelf
和不捕获之间存在细微差别。
因为您使用的是 [weak self]
,所以当您进入该区块时,self
可能是 nil
,所以无论如何您都需要检查一下。一种方法是重复使用可选链接,即:
self?.doSomething()
self?.doSomethingElse()
这是说:
If I have a reference to self, do something. If I still have a
reference to self, do something else.
通过添加一个 strongSelf
变量:
guard let strongSelf = self else {
return
}
strongSelf.doSomething()
strongSelf.doSomethingElse()
...你是说:
do something and do something else if you have a reference to self
,
otherwise do nothing.
所以,你保证如果第一件事发生,第二件事也会发生。您采用的方法将取决于您的应用程序。
场景 1 很适合 [unowned self]
。
在这种情况下,如果队列存在,self 也存在,因此引用 self 而不保留它是安全的。
注意:当您可以确定块的生命周期与捕获的变量直接相关时,您应该仅使用 unowned。在其他情况下,unowned 会导致间歇性崩溃(这真的很难调试)。
此外,unowned 比 weak 更高效,因此在可以安全使用 source.
的情况下应该优先使用
对于场景 2,self 未被我可以确定的任何块捕获,因此您根本不需要担心它。
更新还是不捕获self,定义suggestions字典的闭包应该一调用就执行
有一个声明如下的协议:
typealias SuggestionSourceCallback = ([Suggestion]) -> ()
protocol SuggestionSource {
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback)
}
两个 classes 实现了这个协议。首先 class 异步获取建议(通过 GCD)
final class FisrtClass: SuggestionSource {
private let queue = DispatchQueue(label: "my.app.queue", attributes: [])
private var lastQuery: SuggestionQuery?
// ...
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback) {
self.queue.async { [weak self] in
// capturing strong self
guard let strongSelf = self else {
return
}
// referencing self here, for example
guard self.lastQuery == query else {
return
}
// suggestions is a local variable
var suggestions: [Suggestion] = []
// ...
DispatchQueue.main.async {
callback(suggestions)
}
}
}
}
...而第二个 class 同步执行
final class SecondClass: SuggestionSource {
// ...
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback) {
// ...
callback(self.suggestions[query])
}
}
我的问题是:
- 我应该在
FirstClass
的实现中捕获strongSelf
吗? - 我应该在
SecondsClass
的实现中捕获strongSelf
吗?
更新
补充问题。假设 SecondClass
的 suggestions
作为 static let
,在这种情况下会是什么模式?
final class SecondClass: SuggestionSource {
static let suggestions: [String: [SuggestionQuery]] = {
// ...
}()
// ...
func suggest(_ query: SuggestionQuery, callback: @escaping SuggestionSourceCallback) {
// ...
callback(self.suggestions[query])
}
}
在SecondClass
中,不需要创建strongSelf
变量。你会把它放在哪里?关键是 self
保证不会 nil
无论如何,因为你 运行 在其方法之一的范围内。
你的附加问题也是如此,只是出于不同的原因。 suggestions
现在是 static
,所以用 self
作为前缀是一个语法问题,(我假设你也想用 static
作为 suggest
方法的前缀) .
然而,在 FirstClass
中,捕获 strongSelf
和不捕获之间存在细微差别。
因为您使用的是 [weak self]
,所以当您进入该区块时,self
可能是 nil
,所以无论如何您都需要检查一下。一种方法是重复使用可选链接,即:
self?.doSomething()
self?.doSomethingElse()
这是说:
If I have a reference to self, do something. If I still have a reference to self, do something else.
通过添加一个 strongSelf
变量:
guard let strongSelf = self else {
return
}
strongSelf.doSomething()
strongSelf.doSomethingElse()
...你是说:
do something and do something else if you have a reference to
self
, otherwise do nothing.
所以,你保证如果第一件事发生,第二件事也会发生。您采用的方法将取决于您的应用程序。
场景 1 很适合 [unowned self]
。
在这种情况下,如果队列存在,self 也存在,因此引用 self 而不保留它是安全的。
注意:当您可以确定块的生命周期与捕获的变量直接相关时,您应该仅使用 unowned。在其他情况下,unowned 会导致间歇性崩溃(这真的很难调试)。
此外,unowned 比 weak 更高效,因此在可以安全使用 source.
的情况下应该优先使用对于场景 2,self 未被我可以确定的任何块捕获,因此您根本不需要担心它。
更新还是不捕获self,定义suggestions字典的闭包应该一调用就执行