【Swift】闭包(Closure)
闭包定义
//函数定义
func name(parameters) -> return type {
function body
}
//闭包定义
{ (parameters) -> return type in
statements
}
从上面的函数和闭包的定义中可以看到(parameters) -> return type
是相同的,都是传入参数,可对参数进行操作,然后返回一个值。从定义中可以看出,函数是有name
的,而闭包没有。
那么闭包是如何调用的呢?
//函数
func area(_ width: Double, _ height: Double) -> Double {
return width * height
}
print(area(3, 4))//打印12.0
//闭包
let closureArea = { (width: Double, height: Double) -> Double in
width * height//单行表达式,因为只有一行表达式,所以return可以不写
}
print(closureArea(3, 4))//打印12.0
函数中给这个求面积的函数命名为area
,而闭包是用{}
包裹起来,在这里闭包是没有名字的,所以将闭包赋值相当于给闭包一个名字closureArea
,这样闭包就能像函数一样使用。所以area
约等于closureArea
。
闭包是特殊的函数,当函数作为参数,或返回值,或匿名函数时,称为闭包。
// 为参数,(Double, Double) -> Double为闭包
func area(handle: (Double, Double) -> Double) -> Double {
return handle(3, 4)
}
print(area(handle: {$0 * $1}))//打印12.0
从area
函数看到,函数中只传入一个闭包,然后返回一个Double
值。函数返回值是执行闭包返回的结果,闭包传入(3, 4)
。
闭包对参数做了什么?在调用area
函数时,需要将闭包传入,这里是传入{$0 * $1}
闭包,对参数执行乘法运算。
// 为返回值
func area() -> (Double, Double) -> Double {
return { $0 * $1 }
}
print(area()(3, 4))//打印12.0
从area
函数看到,函数中没有传入参数,只有返回一个闭包(Double, Double) -> Double
,闭包执行了第一个和第二个参数的乘法运算,所以area()
就是一个闭包。
(3, 4)
是闭包传入的参数,所以结果为12.0
。
// 为匿名函数
let areaValue = { (width: Double, height: Double) -> Double in
return width * height
}(3, 4)
print(areaValue)//打印12.0
{ (width: Double, height: Double) -> Double in return width * height }
是一个闭包,这里我们没有把闭包赋值给一个对象,而是直接使用闭包。直接将(3, 4)
传入闭包,所以这个闭包是一个没有名字的闭包,称匿名闭包。
最简闭包
单行表达式,隐式返回值,
return
可以不写。
简化参数
$0
,$1
,代表第0
,1
个参数。
// 闭包
{ (width: Double, height: Double) -> Double in
return width * height
}
// 最简闭包,简化参数
{ $0 * $1 }
尾随闭包
闭包是函数的最后一个参数,称为尾随闭包。
func area(width: Double, height: Double ,handle: (Double, Double) -> Double) -> Double {
return handle(width, height)
}
print(area(width: 3, height: 4, handle: {$0 * $1}))//打印12.0
从area
函数中可以看到,函数中有width
、height
、(Double, Double) -> Double
三个参数,返回一个Double
值。
从函数中的handle(width, height)
中,我们可以看到width
和height
作为参数传到handle
这个闭包里面,闭包根据传值做了一个处理然后返回一个Double
值,具体要对width
、height
做什么处理,需要将闭包作为参数传入area
函数中进行处理。
在例子中,传入了闭包{$0 * $1}
,这个闭包的意思是对传入的第一个和第二个参数做乘法运算,所以打印的结果就是3 * 4
,计算结果12.0
。
//尾随闭包写法
let value1 = area(width: 3, height: 4, handle: {$0 * $1})
// 简化
let value2 = area(width: 3, height: 4) { $0 * $1 }
print(value1, value2)//打印12.0 12.0
自动闭包
自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。
这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。
这种便利语法让你能够省略闭包的花括号{}
,用一个普通的表达式来代替显式的闭包。
// 闭包
func closure(_ closure: () -> String) {
print("closure \(closure())")
}
closure({ "value1!" })
closure { "value2!" }
// 自动闭包
func autoClosure(_ closure: @autoclosure () -> String) {
print("autoClosure \(closure())")
}
autoClosure("v3!")
逃逸闭包
一个接受闭包作为参数的函数,该闭包可能在函数返回后才被调用,也就是说这个闭包逃离了函数的作用域,这种闭包称为逃逸闭包。当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写
@escaping
来明确闭包式允许逃逸,并且在闭包中显式地引用self
。
逃逸闭包的生命周期
- 闭包作为参数传入函数。
- 退出函数。
- 闭包被调用,闭包生命周期结束。
逃逸闭包的生命周期比函数长,函数退出时,逃逸闭包的引用仍被其他对象持有,不会在函数结束时释放。
var array: Array<() -> String> = []
func appendClosure(_ closure: @autoclosure @escaping () -> String) {
array.append(closure)
}
appendClosure("Hello")
let closure = array.first!
print(closure())// 打印 Hello
从appendClosure
函数看出,() -> String
闭包传入到appendClosure
函数中,函数引用了闭包,并将闭包保存到了数组中。退出函数后,闭包还没被调用,所以这个时候闭包就逃逸了。