在
Swift中为哪些嵌套闭包定义捕获的引用?
以此代码为例:
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]用于两个尾随闭包,但是如果我从尾随闭包中的任何一个省略[weak self],编译器是非常高兴的.
所以我留下了3个选项来定义我的捕获列表:
>在每个嵌套的闭包上定义捕获,导致引用
>仅在第一个闭包上定义捕获.
>仅对实际使用引用的最嵌套的闭包定义捕获.
我的问题是:
If I know that my
ExampleDataSource
could benil
at some point,what is the best option to go with?
解决方法
请注意,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,删除对它的所有引用.
调试控制台表示对自身的引用未被保留:
init() loadContent() deinit loadedContent
看起来我们找到了我们的答案!但是为了完成这个目的,让我们来考察替代方案…
如果[weak self]被捕获在只有内部最后面的闭包,GCD将保留ExampleDataSource,直到块完成执行,这就解释了为什么调试看起来像这样:
init() loadContent() loadedContent deinit
同样的事情会发生,如果没有捕获列表被包括,我们从来没有可选地解开自己,尽管编译器,确实试图警告你!
虽然在所有尾随关闭中包含[弱自我]捕获技术上并不是技术上不正确的,但它确实会降低代码的可读性,并且不会感觉到“像Swift一样”.