我在哪里定义Swift中嵌套闭包的捕获引用?
以此代码为例:
addErrorMethod.invoke(rootObject, ErrorType.FIELD, propertyName, message, "");
// ^^^^^^^^^^- assuming it is instance on which we want to invoke this method
在这个例子中,import Foundation
class ExampleDataSource {
var content: Any?
func loadContent() {
ContentLoader.loadContentFromSource() { [weak self] loadedContent in
// completion handler called on background thread
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.content = loadedContent
}
}
}
}
class ContentLoader {
class func loadContentFromSource(completion: (loadedContent: Any?) -> Void) {
/*
Load content from web asynchronously,
and call completion handler on background thread.
*/
}
}
用于两个尾随闭包,但是如果我从任何一个尾随闭包中省略[weak self]
,编译器会非常高兴。
因此,我有3个选项来定义我的捕获列表:
我的问题是:
如果我知道我的
[weak self]
可能会ExampleDataSource
,那么最佳选择是什么?
答案 0 :(得分:16)
重要的是要注意GCD dispatch_async不会导致保留周期。换句话说,当块执行完毕后,GCD将不会保留块内的任何引用。
对于类之间的强引用或分配给实例属性的闭包内的强引用,情况也是如此。 Apple Documentation
话虽如此,在这个例子中,正确的答案是选项2,仅在第一个闭包上定义捕获。
出于测试目的,我稍微修改了代码:
class ExampleDataSource {
init() {
print("init()")
}
deinit {
print("deinit")
}
var content: Any?
func loadContent() {
print("loadContent()")
ContentLoader.loadContentFromSource() { [weak self] loadedContent in
dispatch_async(dispatch_get_main_queue()) {
print("loadedContent")
self?.content = loadedContent
}
}
}
}
class ContentLoader {
class func loadContentFromSource(completion: (loadedContent: Any?) -> Void) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
sleep(5) // thread will hang for 5 seconds
completion(loadedContent: "some data")
}
}
}
首先我创建var myDataSource: ExampleDataSource? = ExampleDataSource()
。
然后我运行myDataSource.loadContent()
。
在完成处理程序有机会运行之前,我设置myDataSource = nil
,删除对它的所有引用。
调试控制台指示未保留对self的引用:
init()
loadContent()
deinit
loadedContent
看起来我们找到了答案!但为了完成起见,让我们测试替代方案......
如果仅在最内部的尾随闭包上捕获[weak self]
,GCD将保留ExampleDataSource
,直到块完成执行,这解释了为什么调试看起来像这样:
init()
loadContent()
loadedContent
deinit
如果没有包含捕获列表,同样的事情将会发生,我们绝不会自行解包self
,尽管编译器会尝试警告你!
虽然在所有尾随闭包中包含[weak self]
抓取在技术上不正确,但它确实会降低代码的可读性,并且不会感觉像Swift那样&#39 ;