闭包表达式(Closure Expression)
{
(参数列表) -> 返回值类型 in
函数体代码
}
复制代码
Swift函数示例:
func sum(_ v1:Int, _ v2: Int) -> Int {
return v1 + v2
}
// 函数调用
let c = sum(10, 20)
复制代码
Swift闭包表达式示例:
// 闭包表达式
var fn = {
(v1:Int,v2:Int) -> Int in
return v1 + v2
}
let s = fn(20,30)
复制代码
闭包表达式的简写:
// 函数
func exech(v1:Int,v2:Int,fn:(Int,Int) ->Int) {
print(fn(v1,v2))
}
exech(v1: 10, v2: 20) { (v1:Int, v2:Int) -> Int in
return v1 + v2
}
exech(v1: 20, v2: 30) { v1,v2 in
v1 + v2
}
exech(v1: 40, v2: 50) {
$0 + $1
}
exech(v1: 50, v2: 60,fn: +)
// 忽略参数
exech(v1: 10, v2: 20) { (_, _) in
500
}
复制代码
输出结果为:
30
50
30
50
70
90
110
500
复制代码
闭包(Closure)
- 网上有两种关于闭包的定义:
- 比较严谨的定义是:一个函数和它所捕获的变量\常量环境组合起来,称为闭包
- 一般定义在函数内部的函数
- 一般它捕获的是外层函数的局部变量\常量
typealias Fn = (Int) -> Int
func getFn() -> Fn {
var num = 0
func plus(_ i:Int) -> Int {
num += i
return num
}
return plus
}
var fn1 = getFn()
var fn2 = getFn()
print(fn1(1)) // 1
print(fn1(2)) // 3
print(fn1(3)) // 6
print(fn1(4)) // 10
print(fn2(5)) // 5
print(fn2(6)) // 11
print(fn2(7)) // 18
复制代码
- 还有一种理解是:可以把闭包想象成是一个类的实例对象
- 内存在堆空间
- 捕获的局部变量\常量是对象的成员(属性)
- 组成闭包的函数是类内部定义的方法
class Closure {
var num = 0
func plus(_ i: Int) -> Int {
num += i
return num
}
}
var cs1 = Closure()
var cs2 = Closure()
print(cs1.plus(1)) // 1
print(cs1.plus(2)) // 3
print(cs1.plus(3)) // 6
print(cs1.plus(4)) // 10
print(cs2.plus(5)) // 5
print(cs2.plus(6)) // 11
print(cs2.plus(7)) // 18
复制代码
注意⚠️:闭包真正捕获变量是在函数返回调用的时候:
typealias Fn = (Int) -> Int
func getFn() -> Fn {
var num = 0
func plus(_ i:Int) -> Int {
num += i
return num
}
// 修改num值
num = 10
return plus
}
复制代码
这时捕获的num初始值就是10而不是0相应的输出结果就会加10
自动闭包
- 函数的参数都为Int数据类型
func getFirstPositive(_ v1:Int, _ v2:Int) -> Int {
return v1 > 0 ? v1 : v2
}
print(getFirstPositive(10,20)) // 10
print(getFirstPositive(-1, 2)) // 2
print(getFirstPositive(0, -5)) // -5
复制代码
- 函数的参数一个为Int类型,另一个是闭包表达式
func getFirstPositive(_ v1:Int, _ v2:()-> Int) -> Int {
return v1 > 0 ? v1 : v2()
}
print(getFirstPositive(10,{20}))
//print(getFirstPositive(-1, 2)) // 会报错
复制代码
- 自动闭包作为参数
func getFirstPositive(_ v1:Int, _ v2:@autoclosure () -> Int) -> Int {
return v1 > 0 ? v1 : v2()
}
// print(getFirstPositive(10,{20}))会报错
print(getFirstPositive(-1, 2)) // 2
func getNum() -> Int {
return 10 + 20
}
print(getFirstPositive(-2, getNum())) // 30
复制代码
- 为了避免与期望冲突,使用@autoclosure的地方最好注释清除:这个值会被推迟执行
- @autoclosure会自动将参数2封装成闭包{2}
- @autoclosure只支持() -> T格式的参数
- @autoclosure并非只支持最后一个参数
- 空合并运算符 ?? 使用了@autoclosure技术,延迟执行增加效率
- 有@autoclosure和无@autoclosure可以构成函数重载
逃逸闭包
- 当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为函数的参数需要标明@escaping,用来指明这个闭包是允许“逃逸”出这个函数。
- 一种能使闭包“逃逸”的方法就是在函数的实现中将闭包存储在函数外的一个变量,在特定的情况下才调用。这种情况下,闭包需要“逃逸”出函数,因为闭包需要在函数返回之后被调用。例如:
var comples:[()->Void] = []
// 包含逃逸闭包的函数
func someFuncWiEscaping(compleHandler:@escaping ()->Void) {
comples.append(compleHandler)
}
复制代码
someFuncWiEscaping(:)函数中的闭包被外部的数组所保存。
- 将一个闭包标记为@escaping意味着你必须在闭包中显式地引用self才能使用当前类中的属性或者方法调用。
- 非逃逸闭包可以隐式引用self。
func someFunWithNoneEscaping(closure:() -> Void) {
closure()
}
class Student {
var x = 10
func doSomething() {
// 如果不显式调用self会编译报错
someFuncWiEscaping { self.x = 100 }
someFunWithNoneEscaping { x = 200 }
}
}
let stu = Student()
stu.doSomething()
print(stu.x)
// 200
comples.first?()
print(stu.x)
// 100
复制代码