闭包是可以在你的代码中被传递和引用的功能性独立模块。Swift中的闭包和C以及OC中的block很像,还有其它语言中的匿名函数也类似。
闭包能够捕获和存储定义在上下文中的任何常量和变量,这也就是所谓的闭合并包裹那些常量和变量,因此被称为“闭包”,Swift能够为你处理所有关于捕获的内存管理的操作。(如果第一次接触闭包,可以先把闭包不准确的理解为一段代码片段,在之后的过程慢慢体会闭包准确的表达方式)
Swift的闭包表达式拥有简洁的风格,鼓励在常见场景中实现简洁,无累赘的语法。
闭包表达式语法
{(parameters) -> (return type) in
//statements
}
闭包表达式参数不提供默认值,in将闭包表达式中参数和返回值说明与具体的闭包语句分开
例如,对names进行排序
letnames = ["Chris","Alex","Ewa","Barry","Daniella"]
letreversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
returns1 > s2
})
闭包有一些简写的形式,比如省略掉参数类型、简写的实际参数名,有些并不很常用,即使遇到了根据情况也能推断出简写的内容。此处不对各种简写形式做陈述,以免看了半天也记不住。一开始记住最基本的书写方式即可。
尾随闭包
如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个呗书写在函数形式参数的括号外面(后面)的闭包表达式:
例如
// someFunctionThatTakesAClosure方法有一个参数,参数类型为闭包
funcsomeFunctionThatTakesAClosure(closure:() -> Void){
// function body goes here
}
// 不使用尾随闭包调用someFunctionThatTakesAClosure方法
someFunctionThatTakesAClosure({
// closure's body goes here
})
// 使用尾随闭包调用someFunctionThatTakesAClosure方法
someFunctionThatTakesAClosure(){
// closure's body goes here
}
如果闭包表达式作为函数的唯一实际参数传入,而你又使用了尾随闭包的语法,那你就不需要函数明后面写圆括号了。someFunctionThatTakesAClosure方法就符合以上标准,因此对该方法的调用也可以写作:
someFunctionThatTakesAClosure{
// closure's body goes here
}
逃逸闭包
@ escaping标明这个闭包是会“逃逸”的,通俗点说就是这个闭包在函数执行完成之后才会被调用。Swift3以后,闭包默认是非逃逸的。
为了解释@ escaping的作用,看以下例子
funcdoWork(block:()->()) {
print("header")
block()
print("footer")
}
doWork{
print("work")
}
//控制台打印的消息如下:
//header
//work
//footer
doWork中整个代码执行都是同步的。我们对doWork做修改,将doWork中的代码改为异步执行。这个时候我们就需要用@ escaping标记表明这个闭包是会“逃逸”的。
func doWork(block: @escaping()->()) {
print("header")
DispatchQueue.main.async {
block()
}
print("footer")
}
doWork{
print("work")
}
//控制台打印的消息如下:
//header
//footer
//work
所以逃逸闭包通俗的来说是,这个闭包在函数执行完成之后才会被调用。
从生命周期看两者区别:
非逃逸闭包的生命周期与函数相同:
[if !supportLists]1、[endif]把闭包最为参数传递给函数
[if !supportLists]2、[endif]函数中调用闭包
[if !supportLists]3、[endif]函数退出。结束
逃逸闭包的生命周期:
[if !supportLists]1、[endif]闭包最为参数传递给函数
[if !supportLists]2、[endif]退出函数
[if !supportLists]3、[endif]闭包被调用,闭包生命周期结束。
即逃逸闭包的生命周期长于函数
循环引用
由于在非逃逸闭包中,闭包是在函数退出前调用的,所以不存在循环引用的问题。
在逃逸闭包中,闭包是在函数退出后执行的。如果闭包语句中持有了self,而self又持有了该闭包的话,就会造成循环引用(类似于OC中的block)。
此时可以使用weak关键字来让闭包不持有self
例如
doWork{[weak self] _ in
guard let strongSelf = self else { return}
print(strongSelf.propertyTest)
}