本文是自己的加深理解和记忆的笔记,非原创。按照自己的理解习惯改写了其他文章的内容(引用资料在最下方),看看就好,最后强调一次,非原创。
[TOC]
1. 什么是逃逸闭包?如何标记?
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.
即:作为一个传入参数,若该闭包在函数返回后才被执行的话,则该闭包就是在逃逸函数。(这样的闭包就是逃逸闭包。)你需要在参数前加上@escaping标记来表明闭包是逃逸的。
2. 什么情况下使用逃逸闭包标记?
-
函数外存储
如果一个函数参数可能导致引用循环,那么它需要被显示地标记出来。@escaping标记可以作为一个警告,来提醒使用这个函数的开发者注意引用关系。
举个例子。此时的
callback
被self
所持有,典型的可能在函数return之后被执行。class SomeClass { var callback:(()->Void)? func doSomething(callback:@escaping ()->Void) { // 加上逃逸修饰词 self.callback = callback } }
-
异步调用
同理,如果闭包被放进
async dispatch queue
,则该闭包也会被queue retain
,同样可能在函式结束后才被执行,因此也算是“逃逸”举个例子。此时的callback被异步调用了
class SomeClass { func doWorkAsync(block: @escaping () -> ()) { // 加上逃逸修饰词 DispatchQueue.main.async { block() } } }
3. 非逃逸闭包有什么限制
不能在函式外储存
不能进
async dispatch queue
-
不能作为其他逃逸闭包函数的参数
把@noescape闭包传到其他@noescape参数是可以的,一连串不会逃逸的传值,最终还是不会逃逸(下面的@noescape会被编译器提示删除,因为swift3开始默认的就是非逃逸闭包)
class SomeClass { func foo( code:@noescape (() -> String)) -> String { return bar(code: code) } func bar( code:@noescape (() -> String)) -> String { return code() } }
4.其他:
从swift3开始,闭包默认为非逃逸闭包。之前则相反,且使用@noescape进行标记(此标记已废弃)。
非逃逸闭包可用被编译器高度优化,快速的执行路径将被作为基准而使用,除非你在有需要的时候显式地使用其他方法。
和弱引用关系:非逃逸闭包中可放心使用self关键字,因为不会在函数外储存,也不会被异步调用。你不需要去使用一个弱引用(weak或unowned)去引用self。
-
在函数内部储存闭包也会被识别成逃逸,虽然并不会(现在的最新swift4仍存在这个问题)
func doSomething(callback:(()->Void) { let c = callback // error: non-escaping parameter 'callback' may only be called c() }