1.什么是闭包
闭包是一个捕获了上下文的常量或者是变量的函数。
-
闭包是
引用类型
-
闭包分为以下几种:
闭包表达式
尾随闭包
逃逸闭包
自动闭包
-
函数是一种特殊的闭包,函数不会捕获值。
-
闭包可以当做变量,也可以当做参数传递
2. 闭包表达式
- 闭包表达式就是一个
匿名函数
,从上下文
中捕获
变量
和常量
。 - 闭包表达式是是swift的语法,使用闭包表达式能更简洁的传达信息。
比如:
{ (age: Int) in
return age
}
优点
:
- 利用上下文推断参数和返回值类型
- 单表达式可以隐士返回,即省略return关键字
- 参数名称的简写
- 尾随闭包表达式
3. 尾随闭包
- 当我们把闭包表达式作为函数的最后一个参数,如果当前的闭包表达式很长,我们可以通过尾随闭包的书写方式提高代码的可读性。
- 尾随闭包的作用域是在当前函数内,
比如说下面的方式,我们可以使用尾随闭包,增加代码的可读性。
var array = [1, 2, 3]
array.sort{(item1 : Int, item2: Int) -> Bool in return item1 < item2 }
array.sort(by: {(item1, item2) -> Bool in return item1 < item2 })
array.sort(by: {(item1, item2) in return item1 < item2 })
array.sort{(item1, item2) in item1 < item2 }
array.sort{ return $0 < $1 } //self
array.sort{ $0 < $1 }
array.sort(by: <)
逃逸闭包
当闭包作为一个实际参数传递给一个函数的时候,并且是在函数返回值后调用了,就称为这个闭包为逃逸闭包。
- 延时调用
- 存储,后面进行调用
逃逸闭包注意问题:
- 逃逸闭包声明时,在参数前写
@escaping
- 逃逸闭包需要注意循环引用:比如显示引用self 、像OCweak调用
loadData = { [weak self] (value) in
print(self.xxx)
}
weak var weakSelf = self
loadData = { (value) in
print(weakSelf.xxx)
}
逃逸闭包案例:
- 在函数返回值后调用
class Teacher{
var complitionHandler: ((Int) -> Void)?
func makeIncrementer(amount: Int, handler: @escaping (Int) -> Void){
var runningTotal = 0
runningTotal += amount
self.complitionHandler = handler
}
func doSomething(){
self.makeIncrementer(amount: 10) {
print($0)
}
}
deinit {
print("LGTeaher deinit")
}
}
var t = Teacher()
t.doSomething()
t.complitionHandler?(10)
- 使用了GCD延长了生命周期
class Teacher{
var complitionHandler: ((Int) -> Void)?
func makeIncrementer(amount: Int, handler: @escaping (Int) -> Void){
var runningTotal = 0
runningTotal += amount
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
handler(runningTotal)
}
}
func doSomething(){
self.makeIncrementer(amount: 10) {
print($0)
}
}
deinit {
print("LGTeaher deinit")
}
}
var t = Teacher()
t.doSomething()
t.complitionHandler?(10)
4. 自动闭包
- 使用
@autoclosure
声明,将当前闭包声明成自动闭包
案例:
函数debugOutPrint当condition为true时,打印传入的参数,但是如果参数是一个函数时,如果codition为false时,出入的函数也会调用,那么此时就可以使用自动闭包,来节约性能。如下列代码
func debugOutPrint(_ condition: Bool , _ message: @autoclosure () -> String){
if condition {
print(message())
}
}
func doSomething() -> String{
//do something and get error message
return "=====NetWork Error Occured======"
}
debugOutPrint(true, doSomething())
debugOutPrint(false, doSomething())
debugOutPrint(true, "Application Error Occured")
打印结果如下:
小结:
-
尾随闭包和逃逸闭包的区别:
-
生命周期不同
- 尾随闭包的生命周期是与函数的生命周期相同
- 逃逸闭包的生命周期比函数的生命周期长
-
循环引用不同
- 非逃逸闭包不会产生循环引用
- 逃逸闭包会产生循环引用
-
非逃逸闭包编译器会在编译时期优化,不会产生retain和release
-
非逃逸闭包的上下文会保存到栈上,而不是堆上(官方文档介绍,但是验证时发现是在堆上)
-
-
函数、闭包都属于引用类型