闭包跟闭包表达式不是一个东西
闭包定义:
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的地方最好明确注释清楚:这个值会被推迟执行










网友评论