闭包只有在函数中做参数的时候才会区分逃逸闭包和非逃逸闭包

在Swift 3 后,传递闭包到函数中的时候,系统会默认为非逃逸闭包类型 (Nonescaping Closures)@noescape,有非逃逸闭包类型必然就有逃逸闭包(Escaping Closures),逃逸闭包在闭包前要添加@escaping关键字

非逃逸闭包的生命周期:1.把闭包作为参数传给函数;2.函数中调用闭包;3.退出函数,闭包生命周期结束

即非逃逸闭包的生命周期与函数相同

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

即逃逸闭包的生命周期长于函数,函数退出的时候,逃逸闭包的引用仍被其他对象持有,不会在函数结束时释放

例子:

非逃逸闭包:

import UIKit
class HttpTool: NSObject {
1    func loadData(callBack:((String)->())){
2       callBack("非逃逸闭包")
3   }
}
ViewController类:
import UIKit

class ViewController: UIViewController

override func
        super.viewDidLoad()
      
    }
    var tools:HttpTool = HttpTool()
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
tools.loadData { (jsonData:String) in
print(jsonData);
        }
    }

}

代码执行顺序1,2,3

当传递闭包参数给函数loadData时,要注意ViewController中的属性tools,虽然闭包会捕获self,但是由于默认闭包参数是非逃逸型,这里可以省略 self, 反正编译器已经知道这里不会有循环引用的潜在风险。




逃逸闭包:

import UIKit
class HttpTool: NSObject {

func loadData(callBack:@escaping((String)->())){
1        DispatchQueue.global().async {
            DispatchQueue.main.async {
2               callBack("这是逃逸闭包")
            }
        }    
3   }
}
ViewController类:
import UIKit

class ViewController: UIViewController

override func
        super.viewDidLoad()
      
    }
    var tools:HttpTool = HttpTool()
    
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
tools.loadData { (jsonData:String) in
print(jsonData);
        }
    }

}

代码执行顺序1,3,2

当传递闭包参数给函数loadData时,要注意ViewController中的属性tools,这里闭包函数的生命周期在函数结束后结束,tools前面省略的self 就有必要做特殊处理,防止造成死循环。

逃逸闭包在闭包前要添加@escaping关键字,这里的闭包的生命周期不可预知


经常使用逃逸闭包的2个场景:
  1. 异步调用: 如果需要调度队列中异步调用闭包,比如网络请求成功的回调和失败的回调,这个队列会持有闭包的引用,至于什么时候调用闭包,或闭包什么时候运行结束都是不确定,上边的例子。
  2. 存储: 需要存储闭包作为属性,全局变量或其他类型做稍后使用,例子待补充。 

以后有用到逃逸闭包的例子,会及时更新,也欢迎看见的小伙伴帮忙补充