http://wiki.jikexueyuan.com/project/swift/
http://www.swift51.com/swift4.0/
尾随闭包:闭包作为一个最后一个参数传给函数
闭包
闭包是可以在你的代码中被传递和引用的功能性独立模块。
闭包能够捕获和存储定义在其上下文中的任何常量和变量的引用。
闭包表达式语法
{ (parameters) -> (return type) in
statements
}
let names = ["Chris","Alex","Ewa","Barry","Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
// 函数作为闭包传入
names.sorted(by: backward)
// 省去函数定义,直接定义闭包
names.sorted(by: { ( s1: String, s2: String) -> Bool in
return s1 > s2
})
// 函数体只有一行,可以写为一行
names.sorted(by: {( s1: String, s2: String) -> Bool in return s1 > s2 } )
// 类型推断,可以省略参数类型及返回值
// 当把闭包作为行内闭包表达式传递给函数,形式参数类型和返回类型都可以被推断出来。所以说,当闭包被用作函数的实际参数时你都不需要用完整格式来书写行内闭包。
names.sorted(by: { s1, s2 in return s1 > s2 } )
// 从单表达式闭包隐式返回,删掉 return 关键字来隐式返回它们单个表达式的结果
names.sorted(by: { s1, s2 in s1 > s2 } )
// 简写的实际参数名
// Swift 自动对行内闭包提供简写实际参数名,你也可以通过 $0 , $1 , $2 等名字来引用闭包的实际参数值。
// 如果你在闭包表达式中使用这些简写实际参数名,那么你可以在闭包的实际参数列表中忽略对其的定义,并且简写实际参数名的数字和类型将会从期望的函数类型中推断出来。 in 关键字也能被省略
// in 关键字也能被省略
names.sorted(by: { $0 > $1 } )
运算符函数
实际上还有一种更简短的方式来撰写上述闭包表达式。Swift 的 String 类型定义了关于大于号( >)的特定字符串实现,让其作为一个有两个 String 类型形式参数的函数并返回一个 Bool 类型的值。这正好与 sorted(by:) 方法的第二个形式参数需要的函数相匹配。因此,你能简单地传递一个大于号,并且 Swift 将推断你想使用大于号特殊字符串函数实现:
reversedNames = names.sorted(by: >)
尾随闭包
如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式:
func someFunctionThatTakesAClosure(closure:() -> Void){
//function body goes here
}
//here's how you call this function without using a trailing closure
someFunctionThatTakesAClosure({
//closure's body goes here
})
//here's how you call this function with a trailing closure instead
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
如果闭包表达式被用作函数唯一的实际参数并且你把闭包表达式用作尾随闭包,那么调用这个函数的时候你就不需要在函数的名字后面写一对圆括号 ( )。
func someFunctionThatTakesAClosure(closure:() -> Void){
//function body goes here
}
someFunctionThatTakesAClosure {
}
func someFunctionThatTakesAClosure(closure:() -> Void, closure2:() -> Void){
//function body goes here
}
someFunctionThatTakesAClosure(closure: {
}) {
}
捕获值
一个闭包能够从上下文捕获已被定义的常量和变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值。
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
作为一种优化,如果一个值没有改变或者在闭包的外面,Swift 可能会使用这个值的拷贝而不是捕获。
Swift也处理了变量的内存管理操作,当变量不再需要时会被释放。如果你分配了一个闭包给类实例的属性,并且闭包通过引用该实例或者它的成员来捕获实例,你将在闭包和实例间建立一个强引用环。
Swift将使用捕获列表来打破这种强引用环。
闭包是引用类型
无论你什么时候安赋值一个函数或者闭包给常量或者变量,你实际上都是将常量和变量设置为对函数和闭包的引用。这上面这个例子中,闭包选择 incrementByTen 指向一个常量,而不是闭包它自身的内容。
这也意味着你赋值一个闭包到两个不同的常量或变量中,这两个常量或变量都将指向相同的闭包。
逃逸闭包
当闭包作为一个实际参数传递给一个函数的时候,我们就说这个闭包逃逸了,因为它可以在函数返回之后被调用。当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写 @escaping 来明确闭包是允许逃逸的。
闭包可以逃逸的一种方法是被储存在定义于函数外的变量里。
闭包被添加到数组中,表示可以逃逸,需要添加@excaping 标明。
比如说,很多函数接收闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,但是闭包要直到任务完成——闭包需要逃逸,以便于稍后调用。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
让闭包 @escaping 意味着你必须在闭包中显式地引用 self ,比如说,下面的代码中,传给 someFunctionWithEscapingClosure(:) 的闭包是一个逃逸闭包,也就是说它需要显式地引用 self 。相反,传给 someFunctionWithNonescapingClosure(:) 的闭包是非逃逸闭包,也就是说它可以隐式地引用 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?()
print(instance.x)
自动闭包
把当做参数传递给函数的表达式,自动打包为闭包,然后把闭包作为参数来操作。
@autoclosure 将传入的表达式自动转为闭包。最终省了一个花括号。
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"
上边的函数 serve(customer:) 接收一个明确的返回下一个客户名称的闭包。下边的另一个版本的 serve(customer:) 执行相同的任务但是不使用明确的闭包而是通过 @autoclosure 标志标记它的形式参数使用了自动闭包。现在你可以调用函数就像它接收了一个 String 实际参数而不是闭包。实际参数自动地转换为闭包,因为 customerProvider 形式参数的类型被标记为 @autoclosure 标记。
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"