一、闭包表达式
{
(参数列表) -> 返回值类型 in
函数体代码
}
1、在Swift中,可有通过func 定义一个函数,也可以通过闭包表达式定义一个函数
func sum(_ a:Int, _ b:Int) ->Int {a + b}
var fn = {
(a: Int, b :Int) -> Int in
a + b
}
//调用
fn(10,20)
2、闭包表达式的简写
func sum(a: Int , b: Int, fn: (Int, Int) -> Int){
print(fn(a,b))
}
//方式一
sum(a: 30, b: 40, fn: {
(a:Int, b:Int) ->Int in
a + b
})
//方式二
sum(a: 40, b: 30, fn: {
a,b in
a + b
})
//方式三
sum(a: 30, b: 40, fn: {$0 + $1})
//方式四
sum(a: 50, b: 40, fn: + )
二、尾随闭包
如果将一个很长的闭包表达式作为函数的最后一个实参,使用尾随闭包也可以增强函数的可读性;尾随闭包是一个被书写在函数调用括号外面的闭包表达式;如果闭包表达式是函数的唯一实参,而且使用了尾随闭包的语法,那就不需要在函数名后边写圆括号。
func sum(a: Int , b: Int, fn: (Int, Int) -> Int){
print(fn(a,b))
}
sum(a: 40, b: 40) { (a,b) -> Int in
return a + b
}
sum(a: 40, b: 30) { $0 + $1 }
// 唯一实参
func exec(fn:(Int,Int) -> Int) {
print(fn(1,4))
}
exec { (a, b) -> Int in
a + b
}
exec(){$0 + $1}
exec{$0 + $1}
三、闭包
一个函数和它所捕获的变量/常量环境组合起来,称为闭包
一般指定义在函数内部的函数
一般它捕获的是外层函数的局部变量/常量;创建一块堆空间
typealias Fn = (Int) -> Int
func getFn() -> Fn {
var num = 0
func plus(_ i: Int) -> Int {
num += i
return num
}
return plus(_:) //返回的plus 和num形成了闭包 ;num 捕获生成堆空间
}
var fn = getFn()
print(fn(1)) //1
print(fn(2)) //3
print(fn(3)) //6
四、自动闭包
自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。
自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。下面的代码展示了闭包如何延时求值。
func getFirstPositive(_ v1: Int ,_ v2: Int) -> Int {
return v1 > 0 ? v1 : v2
}
print(getFirstPositive(10, 20)) // 10
print(getFirstPositive(-2, 20)) // 20
print(getFirstPositive(0, -4)) // -4
函数类型的参数
改成函数类型的参数, 可以v2延迟加载
func getFirstPositive(_ v1:Int,_ v2:()-> Int) ->Int {
return v1 > 0 ? v1 : v2()
}
var a = getFirstPositive(10, {30})
//尾随闭包
var b = getFirstPositive(-20){40}
print(a)
print(b)
//自动闭包
func getFirstPositive(_ v1:Int, _ v2: @autoclosure ()-> Int ) -> Int {
return v1 > 0 ? v1 : v2()
}
print(getFirstPositive(-20, 40))
@autoclosure 会自动将 40 封装成闭包{40}
@autoclosure 只支持 ()-> T 格式的参数
@autoclosure 并非只支持最后一个参数
空合并运算符 ?? 使用了 @autoclosure 技术
有 @autoclosure 、无 @autoclosure,构成了函数重载
五、逃逸闭包
非逃逸闭包、逃逸闭包,一般都是当做参数传递给函数
非逃逸闭包:闭包调用发生在函数结束前,闭包调用在函数作用域内
逃逸闭包: 闭包有可能在函数结束后调用,闭包调用逃离了函数的作用域,需要通过@escaping声明
typealias Fn = () -> ()
//fn 是非逃逸闭包
func test1(_ fn:Fn){
fn()
}
//逃逸闭包
func test2(_ fn:@escaping Fn){
print("开始")
DispatchQueue.global().async {
print("进入")
fn()
}
print("结束")
}
test2 {
print("调用")
}
//开始 结束 进入 调用