swift-逃逸闭包与非逃逸闭包

纪正德
2023-12-01

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

在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 viewDidLoad() {

        super.viewDidLoad()

      

    }

    var tools:HttpTool = HttpTool()

    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        tools.loadData { (jsonData:Stringin

            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 viewDidLoad() {

        super.viewDidLoad()

      

    }

    var tools:HttpTool = HttpTool()

    

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        tools.loadData { (jsonData:Stringin

            print(jsonData);

        }

    }


}

代码执行顺序1,3,2

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

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

经常使用逃逸闭包的2个场景:

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

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










 类似资料: