/*
• 闭包表达式
• 尾随闭包
• 值捕获
• 闭包是引用类型
• 逃逸闭包
• 自动闭包
*/
//闭包表达式
//闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单 明了。下面闭包表达式的例子通过使用几次迭代展示了 sorted(by:) 方法定义和语法优化的方式。每一次迭代 都用更简洁的方式描述了相同的功能。
//sorted 方法
//sorted(by:) 的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的 值进行排序。一旦排序完成,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排 序的新数组。原数组不会被 sorted(by:) 方法修改。
// 下面的闭包表达式示例使用 sorted(by:) 方法对一个 String 类型的数组进行字母逆序排序。以下是初始数 组:
let names = ["Chris","Alex","Ewa","Barry","Daniella"]
//sorted(by:) 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来 表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前 面,排序闭包函数需要返回 true ,反之返回 false 。
func backward(_ s1:String, _ s2:String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)
print(reversedNames)
//闭包的表达式语法
// 闭包表达式语法有如下的一般形式:
// { (parameters) -> returnType in
// statements
// }
//下面的例子展示了之前 backward(_:_:) 函数对应的闭包表达式版本的代码
reversedNames = names.sorted(by: {
(s1: String, s2: String) -> Bool in
return s1 > s2
})
//根据上下文判断类型
reversedNames = names.sorted(by: {
s1, s2 in
return s1>s2
})
//单表达式闭包可以通过省略 return 关键字来隐式返回单行表达式的结果
reversedNames = names.sorted(by: {
s1, s2 in s1 > s2
})
//参数名称缩写
reversedNames = names.sorted(by: {$0 > $1})
//运算符方法
reversedNames = names.sorted(by: >)
//尾随闭包
//如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:
func someFunctionThatTakesAClousure(closure: () -> Void) {
//函数体部分
}
//以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClousure(closure: {
//闭包主体部分
})
//以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClousure() {
//闭包主体部分
}
//方法参数的字符串排序闭包可以改写为:
reversedNames = names.sorted() {$0 > $1}
//如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 () 省略掉:
reversedNames = names.sorted {$0 > $1}
//当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的 Array 类型有一 个 map(_:) 方法,这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一 次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。
//当提供给数组的闭包应用于每个数组元素后,map(_:) 方法将返回一个新的数组,数组中包含了与原数组中的元 素一一对应的映射后的值。
//下例介绍了如何在 map(_:) 方法中使用尾随闭包将 Int 类型数组 [16, 58, 510] 转换为包含对应 String 类型的值的数组 ["OneSix", "FiveEight", "FiveOneZero"] :
let digitNames = [ 0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"]
let numbers = [16,58,510]
//通过传递一个尾随闭包给 numbers 数组的 map(_:) 方法来创建对应的字符串版本数组:
let strings = numbers.map {
(number) -> String in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
}while number > 0
return output
}
//map(_:) 为数组中每一个元素调用了一次闭包表达式。你不需要指定闭包的输入参数 number 的类型,因为可 以通过要映射的数组类型进行推断。
//值捕获:闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
func makeIncrementer(forIncrement amount: NSInteger) -> () -> NSInteger{
var runningTotal = 0
func incrementer() -> NSInteger {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen() // 返回的值为10
incrementByTen() // 返回的值为20
incrementByTen() // 返回的值为30
let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()// 返回的值为7
//闭包是引用类型:无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用
let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()// 返回的值为50
print(alsoIncrementByTen())
//逃逸闭包
//当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping ,用来指明这个闭包是允许“逃逸”出这个函数的。一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
//someFunctionWithEscapingClosure(_:) 函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组 中。如果你不将这个参数标记为 @escaping ,就会得到一个编译错误。
//将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用 self 。比如说,在下面的代码中,传递到 s omeFunctionWithEscapingClosure(_:) 中的闭包是一个逃逸闭包,这意味着它需要显式地引用 self 。相对 的,传递到 someFunctionWithNonescapingClosure(_:) 中的闭包是一个非逃逸闭包,这意味着它可以隐式引用self 。
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
// someFunctionWithEscapingClosure { self.x = 100 }
// someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x)
// 打印出 "200"
completionHandlers.first?()
print(instance.x)
// 打印出 "100"
//自动闭包
//自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显示的闭包
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!" print(customersInLine.count)
// 打印出 "4"
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } ) // 打印出 "Now serving Alex!"
// 如果你想让一个自动闭包可以“逃逸”,则应该同时使用 @autoclosure 和 @escaping 属性。
// customersInLine i= ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))
print("Collected \(customerProviders.count) closures.") // 打印 "Collected 2 closures."
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
// 打印 "Now serving Barry!"
// 打印 "Now serving Daniella!"