swift语言学习-9. 闭包

swift 9 闭包

更好排版:https://www.zybuluo.com/phper/note/78817

闭包在一般的语言中也有,而且用法也差不多,swift中也是有闭包的概念的,这一节来学习swift中的闭包。

什么是闭包

闭包(closure),闭包可以捕获和存储其所在上下文中任意常量和变量的引用。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。

swift中有个sorted函数,用来数组排序。我用这个例子来先说明一下闭包的概念。

  
  
  
  
  1. let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
  2. func backards(s1: String, s2: String) -> Bool {
  3. return s1 > s2
  4. }
  5. var reversed = sorted(names, backards)
  6. println(reversed)
  7. //[Ewa, Daniella, Chris, Barry, Alex]

sorted函数有2个参数,第一个参数是数组,第二个参数传入一个闭包函数。

闭包表达式语法

根据上面的一个简单的例子,我们知道来一个闭包实质上也是一个函数,所以我们总结和精简一下,如何定义一个闭包:

闭包一般是这样定义的:

{ (parameters) -> returnType in
    //函数体
    statements
} 

闭包表达式语法可以使用常量变量inout类型作为参数,不提供默认值。 也可以在参数列表的最后使用可变参数。 元组也可以作为参数和返回值。

那我们按照这个闭包的定义,重现写一下刚才这个排序数组的例子

  
  
  
  
  1. var reversed2 = sorted(names, { (s1: String, s2: String) -> Bool in
  2. return s1 > s2
  3. })
  4. println(reversed2)
  5. //[Ewa, Daniella, Chris, Barry, Alex]

输出的仍然是我们先前的结果。这就是一个闭包的实现。

闭包的函数体部分由关键字in引入。 该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。

我们也可以写成一行:

r = sorted(names, { (s1: String, s2: String) -> Bool in return s1 > s2 } )

根据上下文推断类型

上面的例子中sorted函数,它的第二个参数是类型为(String, String) -> Bool的函数,因此实际上String,String和Bool类型并不需要作为闭包表达式定义中的一部分。 因为所有的类型都可以被正确推断,返回箭头 (->) 和围绕在参数周围的括号也可以被省略:

reversed = sorted(names, { s1, s2 in return s1 > s2 } )

看,是不是简洁多了。

单表达式闭包隐式返回

单行表达式闭包可以通过隐藏return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:

reversed = sorted(names, { s1, s2 in s1 > s2 } )

PS: swift文档中是这样写的,但是发现报错了,不能运行:ambiguous use the operator > (含糊的操作符>)

我擦,更加简洁了有没有???

参数名称缩写

Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。

如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。 in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:

reversed = sorted(names, { $0 > $1 } )

PS: swift文档中是这样写的,但是发现还是报错了,不能运行:ambiguous use the operator > (含糊的操作符>)

运算符函数

用 > 来做运算,更加简洁:

reversed = sorted(names, >)

尾随闭包

如果将一个很长的闭包表达式作为一个参数参数一个函数,这样可读性就会很差,而且写起来很蛋疼。所以,就出现了尾随闭包,尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

  
  
  
  
  1. // 以下是不使用尾随闭包进行函数调用
  2. someFunctionThatTakesAClosure({
  3. // 闭包主体部分
  4. })
  5. // 以下是使用尾随闭包进行函数调用
  6. someFunctionThatTakesAClosure() {
  7. // 闭包主体部分
  8. }

所以刚才的那个例子,也能这样写:

  
  
  
  
  1. reversed = sorted(names) {s1, s2 in return s1 > s2}

当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。

来一个尾随闭包原始的写法:

  
  
  
  
  1. reversed = sorted(names) {
  2. (s1: String, s2: String) -> Bool in return s1 > s2
  3. }

捕获值

闭包可以在其定义的上下文中捕获常量或变量。 即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

看这个例子:

  
  
  
  
  1. func makeIncrementor(forIncrement amount: Int) -> () -> Int {
  2. var runningTotal = 0
  3. func incrementor() -> Int {
  4. runningTotal += amount
  5. return runningTotal
  6. }
  7. return incrementor
  8. }

一个函数makeIncrementor ,它有一个Int型的参数amout, 并且它有一个外部参数名字forIncremet,意味着你调用的时候,必须使用这个外部名字。返回值是一个()-> Int的函数。

函数题内,声明了变量runningTotal 和一个函数incrementor。

incrementor函数并没有获取任何参数,但是在函数体内访问了runningTotal和amount变量。这是因为其通过捕获在包含它的函数体内已经存在的runningTotal和amount变量而实现。

由于没有修改amount变量,incrementor实际上捕获并存储了该变量的一个副本,而该副本随着incrementor一同被存储。

所以我们调用这个函数:

let incrementByTen = makeIncrementor(forIncrement: 10)

incrementByTen()
// 返回的值为10
incrementByTen()
// 返回的值为20
incrementByTen()
// 返回的值为30

会累加。

闭包是引用类型

上面的例子中,incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。这是因为函数和闭包都是引用类型

无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen指向闭包的引用是一个常量,而并非闭包内容本身。

这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:

let alsoIncrementByTen = incrementByTen
alsoIncrementByTen()
// 返回的值也为50

你可能感兴趣的:(swift语言学习-9. 闭包)