Swift 逃逸闭包和非逃逸闭包
在Swift中,闭包可以定义为自包含的代码块,可以在方法中传递或在我们的代码中使用。然而,我们可以定义两种类型的闭包,即逃逸闭包和非逃逸闭包。在Swift 5中,默认情况下,闭包参数是非逃逸的。
闭包还可以在函数体内执行;如果我们需要逃逸闭包,我们可以将其标记为@escaping。闭包可以捕获和存储来自其定义上下文的任何常量和变量的引用。这被称为闭包对这些常量和变量的封闭。
在本文中,我们将讨论逃逸闭包和非逃逸闭包。我们还将讨论为什么我们应该在我们的代码中使用逃逸闭包。
非逃逸闭包
当我们将闭包作为参数传递给函数时,函数执行闭包并返回给编译器。在这种情况下,一旦闭包被执行并执行结束,传递的闭包就会超出范围,因此在内存中不再存在。非逃逸闭包在其生命周期范围内过渡到以下状态。
- 将闭包作为函数参数传递给函数调用。
- 执行函数体。
- 与函数体一起执行闭包。
- 函数将编译器返回。
示例
让我们考虑以下示例,其中函数接受一个非逃逸闭包作为参数。
func calculate_Sum(_ array:Array, performSum:((Int) -> Void)){
var sum = 0
for i in array{
sum = sum + i
}
performSum(sum)
}
func calculation(){
var array = [0,1,2,3,4,5,6,7,8,9]
calculate_Sum(array) { (sum) in
debugPrint(sum)
}
}
在上面的示例中,我们将闭包传递给了一个函数,在同一个函数中将传递的数组的总和打印出来。这里,函数calculate_Sum在函数体的末尾执行闭包。
在这里,我们没有跳过闭包的执行。因此,一旦闭包执行完毕,它在内存中就不存在了。
逃逸闭包
逃逸闭包与非逃逸闭包不同,因为我们可以保留逃逸闭包以便以后执行它们。同时,函数体可以执行并返回给编译器。逃逸闭包的作用域存在,并且在执行之前将其存在在内存中。我们可以根据需要使用逃逸闭包。以下是逃逸闭包的几种方式:
存储
逃逸闭包的一种方式是将闭包保存在函数外定义的变量中,并在以后执行它。这种方法用于需要使用启动异步操作并接受闭包参数作为完成处理程序的函数的情况。函数在启动操作后返回,但是闭包参数被保存以便以后调用。
考虑以下示例,其中calculate_Sum()接受一个逃逸闭包作为参数,保存在completionHandler中,它是一个类似闭包类型的变量。在函数执行后,会调用completionHandler。
var complitionHandler: ((Int)->Void)?
var sum = 0
func calculate_Sum(_ array:Array, performSum: @escaping ((Int) -> Void)){
for i in array{
sum = sum + i
}
complitionHandler = performSum
}
func calculation(){
sum = 0
var array = [0,1,2,3,4,5,6,7,8,9]
calculate_Sum(array) { (sum) in
debugPrint(sum)
}
}
calculation()
complitionHandler!(sum)
异步执行
当我们在DispatchQueue上异步执行闭包时,闭包会被队列保存在内存中以便将来使用。在这种情况下,我们无法确定闭包何时会被执行。考虑以下示例。
func calculate_Sum(_ array:Array, performSum: @escaping ((Int) -> Void)){
var sum = 0
for i in array{
sum = sum + i
}
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) {
performSum(sum)
}
}
func calculation(){
let array = [0,1,2,3,4,5,6,7,8,9]
calculate_Sum(array) { (sum) in
debugPrint(sum)
}
}
calculation()