「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战」。
自动闭包
有下面一个例子,当condition为true时,会打印错误信息,即如果是false,当前条件不会执行
//1、condition为false时,当前条件不会执行
func debugOutPrint(_ condition: Bool, _ message: String){
if condition {
print("cjl_debug: (message)")
}
}
debugOutPrint(true, "Application Error Occured")
- 如果字符串是在某个业务逻辑中获取的,会出现什么情况?
func debugOutPrint(_ condition: Bool, _ message: String){
if condition {
print("cjl_debug: (message)")
}
}
func doSomething() -> String{
print("doSomething")
return "Network Error Occured"
}
<!--如果传入true-->
debugOutPrint(true, doSomething())
//打印结果
doSomething
cjl_debug: Network Error Occured
<!--如果传入false-->
debugOutPrint(false, doSomething())
//打印结果
doSomething
通过结果发现,无论是传入true还是false,当前的方法都会执行,如果这个方法是一个非常耗时的操作,这里就会造成一定的资源浪费。所以为了避免这种情况,需要将当前参数修改为一个闭包
- 【修改】 :将
message参数修改成一个闭包,需要传入的是一个函数
//3、为了避免资源浪费,将当前参数修改成一个闭包
func debugOutPrint(_ condition: Bool, _ message: () -> String){
if condition {
print("cjl_debug: (message())")
}
}
func doSomething() -> String{
print("doSomething")
return "Network Error Occured"
}
debugOutPrint(true, doSomething)
修改后运行结果如下
- 如果此时传入一个
string,又需要如何处理呢?
可以通过@autoclosure将当前的闭包声明成一个自动闭包,不接收任何参数,返回值是当前内部表达式的值。所以当传入一个String时,其实就是将String放入一个闭包表达式中,在调用的时候返回
//4、将当前参数修改成一个闭包,并使用@autoclosure声明成一个自动闭包
func debugOutPrint(_ condition: Bool, _ message: @autoclosure() -> String){
if condition {
print("cjl_debug: (message())")
}
}
func doSomething() -> String{
print("doSomething")
return "Network Error Occured"
}
<!--使用1:传入函数-->
debugOutPrint(true, doSomething())
<!--使用2:传入字符串-->
debugOutPrint(true, "Application Error Occured")
<!--打印结果-->
doSomething
cjl_debug: Network Error Occured
cjl_debug: Application Error Occured
自动闭包就相当于
debugOutPrint(true, "Application Error Occured")
相当于用{}包裹传入的对象,然后返回{}内的值
{
//表达式里的值
return "Network Error Occured"
}
总结
-
逃逸闭包:一个接受闭包作为参数的函数,逃逸闭包可能会在函数返回之后才被调用,即闭包逃离了函数的作用域,例如网络请求,需要在形参前面使用@escaping来明确闭包是允许逃逸的。- 一般
用于异步函数的回调,比如网络请求 - 如果标记为了@escaping,必须在闭包中显式的引用self
- 一般
-
非逃逸闭包:一个接受闭包作为参数的函数,闭包是在这个函数结束前内被调用,即可以理解为闭包是在函数作用域结束前被调用 -
为什么要区分
@escaping 和 @nonescaping?- 1、为了内存管理,
闭包会强引用它捕获的所有对象,这样闭包会持有当前对象,容易导致循环引用 - 2、
非逃逸闭包不会产生循环引用,它会在函数作用域内使用,编译器可以保证在函数结束时闭包会释放它捕获的所有对象 - 3、
使用非逃逸闭包可以使编译器应用更多强有力的性能优化,例如,当明确了一个闭包的生命周期的话,就可以省去一些保留(retain)和释放(release)的调用 - 4、
非逃逸闭包它的上下文的内存可以保存在栈上而不是堆上
- 1、为了内存管理,
-
总结:如果没有特别需要,
开发中使用非逃逸闭包是有利于内存优化的,所以苹果把闭包区分为两种,特殊情况时再使用逃逸闭包