闭包
跟闭包表达式
不是一个东西
闭包定义:
1、一个函数和它所捕获的变量、常量环境组合起来,称之为闭包
- 一般指定义在函数内部的函数
- 一般它捕获的是外层函数的局部变量、常量
func fn() -> () -> () {
var a = 10
func fn1() {
a = 11
}
return fn1
}
例子
typealias Fn = (Int) -> Int
func getFn() -> Fn {
var num = 0
func plus(_ i: Int) -> Int {
num += 1
return num
}
return plus
} // 返回的plus和num形成了闭包
var fn = getFn()
print(fn(1)) //1
print(fn(2)) //3
print(fn(3)) //6
print(fn(4)) //10
num
是函数局部变量,正常来说调用getFn
之后分配一段连续的栈空间,空间内部有8
个字节用来存储num
变量0
。
一旦return
返回之后意味着函数调用结束,栈空间会被回收,num
变量会被销毁,既然栈空间被回收,为何能调用fn
。
全局变量fn
内部持有函数plus
的地址信息。
plus
持有了局部变量num
,根据汇编代码来看,getFn
函数内部调用了swift_allocObject
,申请了一块堆空间给变量num
,使得不会因为函数调用之后被销毁。
可以将闭包想象成一个类的实例对象
- 内存在堆空间
- 捕获的局部变量、常量就是对象的成员(存储属性)
- 组成闭包的函数就是类内部定义的方法
class Closure {
var num = 0
func plus(_ i: Int) -> Int {
num += i
return num
}
}
var cs1 = Closure()
print(cs1.plus(1)) //1
print(cs1.plus(2)) //3
print(cs1.plus(3)) //6
如果返回值是函数类型,那么参数的修饰要保持统一
func add(_ num: Int) -> (inout Int) -> Void {
func plus(v: inout Int) {
v += num
}
return plus
}
var num = 5
add(20)(&num)
print(num)
自动闭包
看个例子
//如果第1个数大于0,返回第1个数,否则返回第2个数
func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
return v1 > 0 ? v1 : v2
}
getFirstPositive(10, 20) // 10
getFirstPositive(-2, 20) // 20
getFirstPositive(0, -4) // -4
func getNumber() -> Int {
let a = 10
let b = 11
print("test-----")
return a + b
}
//返回第1个参数,同时调用了第2个参数的函数实现,输出print("test-----")
getFirstPositive(10, getNumber()) //10
上面函数调用中,第1个参数10大于0,所以返回10,那么第2个参数getNumber()
的调用就没必要,浪费资源,如何优化?
//将v2变成一个函数
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int {
return v1 > 0 ? v1 : v2()
}
getFirstPositive(-10) {
let a = 10
let b = 11
print("test-----111")
return a + b
} //第1个参数小于0,返回第2个参数,调用了print("test-----111")
getFirstPositive(10) {
let a = 10
let b = 11
print("test-----222")
return a + b
} //第1个参数大于0,返回第1个参数,没有输出print("test-----111"),说明第2参数没调用
上面的例子中,如果代码比较精简的话,可读性不高:
getFirstPositive1(10, {20})
getFirstPositive1(10) {20}
Swift
提供了一个语法:自动闭包
添加关键词:@autoclosure
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)
注意点
1、@autoclosure
会自动将20封装成闭包{ 20 }
2、@autoclosure
只支持 () -> T
格式的参数
3、@autoclosure
并非只支持最后1个参数
func getFirstPositive3(_ v1: Int, _ v2: @autoclosure () -> Int, _ v3: Int, _ v4: Int) -> Int {
return v1 > 0 ? v1 : v2()
}
getFirstPositive3(10, 20, 30, 40)
4、空合并运算符??
使用了@autoclosure
技术
5、有@autoclosure
、无@autoclosure
构成了函数重载
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int {
return v1 > 0 ? v1 : v2()
}
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20) //调用 @autoclosure 的getFirstPositive
getFirstPositive(10, {30}) //调动无 @autoclosure 的getFirstPositive
6、为了避免与期望冲突,使用了@autoclosure
的地方最好明确注释清楚:这个值会被推迟执行