闭包基础知识

2018-04-24  本文已影响2人  小橘子成长记

Swift还有另一个对象,可以用来将代码分解为可重用的块:闭包。

闭包只是一个没有名称的函数;你可以将其分配给一个变量,并像其他任何对象一样传值给它。本章向你展示了闭包是多么的方便和有用。

闭包基础知识

闭包之所以如此命名,是因为它们有能力“关闭”自身范围内的变量和常量。简单的说就是闭包可以访问、存储和操作来自周围上下文的任何变量或常量的值。包括被闭包内的变量和常量。

你可能会问:“如果闭包是没有名称的函数,那么你如何使用它们呢?”要使用闭包,首先必须将其分配给一个变量或常量。

这里是一个变量的声明,它的类型是一个闭包:

var multiplyClosure: (Int, Int) -> Int

multiplyClosure接受两个Int值并返回一个Int,注意这与函数的变量声明完全相同。就像我说的,闭包只是一个没有名字的函数。闭包的类型是函数类型。

你可以将一个闭包分配给这个变量:

multiplyClosure = { (a: Int, b: Int) -> Int in
    return a * b 
} 

这看起来类似于函数声明,但是有一个细微的区别。有相同的参数列表,->符号和返回类型。但是在闭包的情况下,这些元素出现在大括号内,并且在返回类型之后有一个in关键字。

定义了闭包变量之后,就可以使用它,它就像一个函数一样:

let result = multiplyClosure(4, 2)

结果等于8。尽管如此,还是有细微的差别。

注意,闭包没有外部名称的参数。你不能把它们当作函数设置参数名称。

简写语法

与函数相比,闭包被设计为轻量级。有很多方法可以缩短它们的语法。

如果闭包包含一个返回语句,则可以省略return关键字,例如:

multiplyClosure = { (a: Int, b: Int) -> Int in
    a*b 
} 

你可以使用Swift的类型推断,通过删除类型信息来进一步缩短语法:

multiplyClosure = { (a, b) in
    a*b 
} 

请记住,你已经声明了multiplyClosure作为一个闭包,使用两个Int并返回一个Int,因此你可以让Swift为你推断这些类型。

甚至可以省略参数列表。Swift允许你按编号引用每个参数,从0开始,如下所示:

multiplyClosure = {
    $0 * $1 
} 

参数列表、返回类型和关键字都消失了,这样才能使用编号参数。

如果参数列表长得多,那么记住每个编号的参数的意义就会很混乱。在这些情况下,你应该使用命名的语法。

如下面的代码:

func operateOnNumbers(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) -> Int {
  let result = operation(a, b)
  print(result)
  return result
} 

声明一个名为operateOnNumbers的函数,它将Int值作为前两个参数。第三个参数命名为operation,它是一个函数类型。operateOnNumbers本身返回一个整数。

然后operateOnNumbers的参数可以使用闭包,比如:

let addClosure = { (a: Int, b: Int) in
    a+b 
} 
operateOnNumbers(4, 2, operation: addClosure)

记住,闭包只是没有名称的函数。所以你不用惊讶他可以像函数一样作为参数传递函数,比如

func addFunction(_ a: Int, _ b: Int) -> Int {
    return a + b 
} 
operateOnNumbers(4, 2, operation: addFunction)

无论operation是一个函数还是一个闭包,operateOnNumbers的调用都是一样的

闭包语法的力量再次派上用场。你可以在operateOnNumbers函数中来定义闭包,如下所示:

operateOnNumbers(4, 2, operation: { (a: Int, b: Int) -> Int in
    return a + b 
}) 

没有必要定义闭包并将其分配给局部变量或常量。你可以简单地在函数参数中声明闭包,将其传递到函数中作为参数!

简化闭包语法以删除大量的样板代码。因此,你可以将上述内容减少到以下代码:

operateOnNumbers(4, 2, operation: { $0 + $1 })

甚至可以更进一步。+运算符只是一个包含两个参数并返回一个结果的函数,所以你可以写:

operateOnNumbers(4, 2, operation: +)

还有一种方法可以简化语法,但是只有当最终的参数传递给函数时才能完成。在这种情况下,你可以将闭包移到函数调用之外:

operateOnNumbers(4, 2) {
    $0 + $1 
} 

这可能看起来很奇怪,但它与前面的代码片段相同,只不过你已经删除了operation,并在函数调用参数列表之外拉了括号。这称为尾随闭包语法。

没有返回值的闭包
到目前为止,你所看到的所有闭包都使用了一个或多个参数并返回了值。但是就像函数一样,闭包有时不需要做这些事情。下面是如何声明一个不带参数且不返回任何参数的闭包:

let voidClosure: () -> Void = {
   print("Swift Apprentice is awesome!")
}
voidClosure()

闭包的类型是()-> Void。空括号表示没有参数。你必须声明一个返回类型,然后Swift才知道你正在声明一个闭包。这就是Void的用处所在,它的意思正是就是它的名字所表示的:这是一个不返回任何东西的闭包。

注意:Void实际上只是()的一个类型别名。这意味着你可以编写()-> Void as () ->()。然而,函数的参数列表必须始终被括号括起来,所以Void ->()或Void -> Void都是无效的。

闭包范围中的变量或常量

最后,让我们回到闭包的定义特性:它可以从它自己的范围内访问变量和常量。

例如,使用以下闭包:

var counter = 0
let incrementCounter = {
    counter += 1 
} 

incrementCounter相当简单:counter变量增加。counter变量是在闭包之外定义的。闭包可以访问变量,是因为闭包定义在与变量相同的范围内。闭包可以捕获counter变量。它对变量的更改在闭包内并外部都可见。

比方说,你调用闭包5次,就像这样:

incrementCounter()
incrementCounter()
incrementCounter()
incrementCounter()
incrementCounter()

在这五个incrementCounter之后,counter将等于5。

闭包可以用于从封闭范围内捕获变量。例如,你可以编写以下函数:

func countingClosure() -> () -> Int {
   var counter = 0
   let incrementCounter: () -> Int = {
       counter += 1 
       return counter
   }
   return incrementCounter
}

该函数不接受参数,但返回一个闭包。它返回的闭包不接受参数但返回Int。

这个函数返回的闭包每次只会增加它的内部counter。每次调用这个函数时,都会得到一个不同的counter。

例如,可以这样使用:

let counter1 = countingClosure()
let counter2 = countingClosure()
counter1() // 1
counter2() // 1
counter1() // 2
counter1() // 3
counter2() // 2

这个函数创建的两个计数器相互独立,独立计数。整洁!

上一篇 下一篇

猜你喜欢

热点阅读