Swift 逃逸闭包

非逃逸闭包
非逃逸闭包的生命周期:

  1. 把闭包作为参数传递给函数。
  2. 函数中运行该闭包。
  3. 退出函数。

非逃逸闭包被限制在函数内,当函数退出时,该闭包的引用计数不会增加,也就是说其引用计数在进入函数和退出函数时保持不变。

逃逸闭包

  1. 闭包作为参数传递给函数;
  2. 退出函数;
  3. 闭包被调用,闭包生命周期结束

闭包参数默认是非逃逸类型。如果需要其逃逸类型的闭包,可以使用关键字@escaping

举个例子我们常用的Masonry或者SnapKit的添加约束的方法就是非逃逸的。因为这闭包马上就执行了。
比如这个Alamofire里的处理返回json的completionHandler闭包就是逃逸的,网络请求结束后的回调的闭包是逃逸的,因为发起请求后过了一段时间后这个闭包才执行。

//这是一个非逃逸闭包,闭包closure 在函数执行的时候就调用了 
func callAction(closure:(() -> Void)) {
    closure()
}

//这是一个逃逸闭包,闭包closure 到了另一个作用域,跟当前函数是异步的,逃逸闭包必须用@escaping来修饰。因为默认是 @noescaping
func callAction(closure:@escaping (() -> Void)) {
    DispatchQueue.main.async {
        closure()
    }
}

逃逸闭包使用场景
异步调用:如果需要调度队列中异步调用闭包,这个队列会持有闭包的引用,至于什么时候调用闭包,或者闭包什么时候运行结束都是不可预知的。
存储:需要存储闭包作为属性,全局变量或者其他类型做稍后使用。

为什么要分逃逸闭包和非逃逸闭包
为了管理内存,闭包会强引用它捕获的所有对象,比如你在闭包中访问了当前控制器的属性、函数,编译器会要求你在闭包中显示 self 的引用,这样闭包会持有当前对象,容易导致循环引用。

非逃逸闭包不会产生循环引用,它会在函数作用域内释放,编译器可以保证在函数结束时闭包会释放它捕获的所有对象;使用非逃逸闭包的另一个好处是编译器可以应用更多强有力的性能优化,例如,当明确了一个闭包的生命周期的话,就可以省去一些保留(retain)和释放(release)的调用;此外非逃逸闭包它的上下文的内存可以保存在栈上而不是堆上。

综上所述,如果没有特别需要,开发中使用非逃逸闭包是有利于内存优化的,所以苹果把闭包区分为两种,特殊情况时再使用逃逸闭包。

你可能感兴趣的:(Swift 逃逸闭包)