闭包是自包含的代码块,可以在代码中被传递和使用。与C、OC中的block相似。
闭包可以捕获和存储其所在上下文中任意常量和变量的引用。
-
闭包采取如下三种形式之一:
- 全局函数是一个有名字但不会捕获任何值的闭包
- 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
- 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
-
闭包表达式
闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始
单行表达式闭包可以通过省略return关键字来隐式返回单行表达式的结果
-
自动为内联闭包提供了参数名称缩写功能,可以直接通过$0,$1,$2来顺序调用闭包的参数
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] func backward(_ s1: String, _ s2: String) -> Bool{ return s1 > s2 } var reversedNames = names.sorted(by: backward) reversedNames = names.sorted(by: {(s1: String, s2: String) -> Bool in return s1 > s2 }) //根据上下文推断类型 reversedNames = names.sorted(by: {s1, s2 in return s1 > s2}) //单表达式闭包隐式返回 reversedNames = names.sorted(by: {s1, s2 in s1 > s2}) //参数名称缩写 reversedNames = names.sorted(by: {$0 > $1}) //运算符方法 reversedNames = names.sorted(by: >)
-
尾随闭包
- 如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。
- 格式:
func someFunctionThatTakesAClosure(closure: () -> Void){
//函数体部分
}
//以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure {
//闭包主体部分
}
//以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(){
//闭包主体部分
}
-
值捕获
- 可以捕获值的闭包的最简单形式是嵌套函数。
- 为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,swift可能会改为捕获并保存一份对值的拷贝;swift也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。
- 如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或者成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。swift使用捕获列表来打破这种循环强引用。
-
闭包是引用类型
- 函数和闭包都是引用类型。无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用。
-
逃逸闭包
- 当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。
- 定义接受闭包作为参数的函数时,可以在参数名之前标注@escaping,用来指明这个闭包是允许“逃逸”出这个函数的。
- 标记为逃逸闭包,就需要在闭包中显式的引用self。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void){
completionHandlers.append(completionHandler)
}
func someFunctionWithNoneEscapingClosure(closure: () -> Void){
closure()
}
class SomeClass{
var x = 10
func doSomething() {
someFunctionWithEscapingClosure {
self.x = 100
}
someFunctionWithNoneEscapingClosure {
x = 200 } }}
let instance = SomeClass()
instance.doSomething()
print(instance.x)completionHandlers.first?()
打印:200
print(instance.x)
打印:100
-
自动闭包
自动创建的闭包;用于包装传递给函数作为参数的表达式;
-
不接受任何参数,被调用时,会返回被包装在其中的表达式的值;这种便利语法可以省略花括号,用一个普通的表达式来代替显式的闭包。
//闭包如何延时求值 var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] print(customersInLine.count) //打印出 "5" let customerProvider = {customersInLine.remove(at: 0)} print(customersInLine.count) //打印出 "5" print("now serving \(customerProvider())!") //打印出 "now serving Chris!" print(customersInLine.count) //打印出“4”
func serve(customer customerProvider: () -> String){
print("now serving \(customerProvider())!")
}
serve(customer: {customersInLine.remove(at: 0)})
//自动闭包
func serve(customer customerProvider: @autoclosure () -> String){
print("now serving \(customerProvider())!")}
serve(customer: customersInLine.remove(at: 0))
注意:过度使用auto closures会让代码变得难以理解。上下文和函数名应该能够清晰的表明求值是被延时执行的