闭包表达式
sort 函数
下面的闭包表达式示例使用 sort 函数对一个String类型的数组进行字母逆序排序,以下是初始数组值:
var names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
排序函数有两个参数:
- 已知类型值的数组。
- 一个闭包,采用相同类型的数组的内容的两个参数,并返回一个布尔值来表示是否将第一个值在排序时放到第二个值的前面或是后面。如果第一个值应该出现第二个值之前,闭包需要返回true,否则返回false。
提供排序闭包的一种方式是撰写一个符合其类型要求的普通函数,并将其作为 sort 函数的第二个参数传入:
func backwards(s1: String, s2: String) -> Bool { return s1 > s2 } sort(names, backwards) // ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
闭包表达式语法
闭包表达式语法有如下一般形式:
{ (parameters) -> returnType in statements }
闭包表达式语法可以使用常量、变量和 inout 类型作为参数,但不提供默认值。 也可以在参数列表的最后使用可变参数。元组也可以作为参数和返回值。
下面的例子展示了之前 backwards 函数对应的闭包表达式版本的代码:
sort(names, { (s1: String, s2: String) -> Bool in return s1 > s2 })
闭包的函数体部分由关键字 in 引入。 该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
因为这个闭包的函数体部分如此短以至于可以将其改写成一行代码:
sorted(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )
sorted(names, { s1, s2 in return s1 > s2 } ) // 根据上下文推断类型 sorted(names, { s1, s2 in s1 > s2 } ) // 单行表达式闭包可以省略 return sorted(names, { $0 > $1 } ) // 参数名简写 sorted(names, >) // 运算符函数
尾随闭包
Trailing 闭包是一个书写在函数括号之外(之后)的闭包表达式,函数支持将其作为最后一个参数调用。
func someFunctionThatTakesAClosure(closure: () -> ()) { // 函数体部分 } // 以下是不使用 trailing 闭包进行函数调用 someFunctionThatTakesAClosure({ // 闭包主体部分 }) // 以下是使用 trailing 闭包进行函数调用 someFunctionThatTakesAClosure() { // 闭包主体部分 }
注意:如果函数只需要闭包表达式一个参数,当您使用 trailing 闭包时,您甚至可以把 () 省略掉。
在上例中作为 sort 函数参数的字符串排序闭包可以改写为:
reversed = sorted(names) { $0 > $1 }
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] let strings = numbers.map { (var number) -> String in var output = ""
while number > 0 { output = digitNames[number % 10]! + output number /= 10 } return output } // strings 常量被推断为字符串类型数组,即 String[] // 其值为 ["OneSix", "FiveEight", "FiveOneZero"]
捕获 (Caputure)
闭包可以在其定义的上下文中捕获常量或变量。 即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数体内的函数。 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
下例为一个叫做 makeIncrementor 的函数,其包含了一个叫做 incrementor 嵌套函数。 嵌套函数 incrementor 从上下文中捕获了两个值,runningTotal 和 amount。 之后 makeIncrementor 将 incrementor 作为闭包返回。 每次调用 incrementor 时,其会以 amount 作为增量增加 runningTotal 的值。
func makeIncrementor(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementor() -> Int { runningTotal += amount return runningTotal } return incrementor }
下面为一个使用 makeIncrementor 的例子:
let incrementByTen = makeIncrementor(forIncrement: 10)
该例子定义了一个叫做 incrementByTen 的常量,该常量指向一个每次调用会加10的 incrementor 函数。 调用这个函数多次可以得到以下结果:
incrementByTen() // 返回的值为10 incrementByTen() // 返回的值为20 incrementByTen() // 返回的值为30
如果您创建了另一个 incrementor,其会有一个属于自己的独立的 runningTotal 变量的引用。 下面的例子中,incrementBySevne 捕获了一个新的 runningTotal 变量,该变量和 incrementByTen 中捕获的变量没有任何联系:
let incrementBySeven = makeIncrementor(forIncrement: 7) incrementBySeven() // 返回的值为7 incrementByTen() // 返回的值为40
闭包是引用类型
上面的例子中,incrementBySeven 和 incrementByTen 是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。 这是因为函数和闭包都是引用类型。
无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen 指向闭包的引用是一个常量,而并非闭包内容本身。
这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
let alsoIncrementByTen = incrementByTen alsoIncrementByTen() // 返回的值为50
2015-03-20
17:12:50