Swift 中的闭包(Closures)

闭包

闭包是自包含的函数代码块,可以在代码中被传递和使用。 Swift 中的闭包与Objective-C 中的Blocks比较相似。

闭包可以捕获和存储其所在上下文中的变量和常量。所谓闭包就是闭合和包裹这这些常量和变量。在使用过程中会发现Swift中的函数和闭包很像,因为函数就是一种特殊的闭包。

sorted函数

Swift 标准库中提供了一个sorted函数,会根据我们自己提供的具有排序功能的闭包函数对数组进行排序,然后会返回一个排序完成的新数组。

下面我们根据一个列子,逐渐学习闭包的特性。并运用sorted函数对一个数字数组进行排序。

let numbers = [1, 2, 3, 5, 8]

sorted函数需要传入两个参数

  1. 需要进行指定操作的数组
  2. 闭包函数,对数组进行操作。(该闭包函数需要传入与数组类型相同的两个值,并返回一个Bool类型的值来告诉sorted函数进行比较的两个值哪一个在前哪一个在后,如果返回true,则第一个值在前,反之亦然)

该例子对一个Int数组进行排序,所以闭包函数的类型需为:(String, String) -> Bool

首先,我们看一下最完整的做法:

func backwords(num1: Int, num2: Int) -> Bool{
	return num1 > num2
}
var result = sorted(numbers,backwords)
//执行到这里result的值为[8, 5, 3, 2, 1]

闭包表达式语法

正如你所见,虽然这种方式能得到结果,但是这种方式实在太冗长了。我们可以用闭包表达式来缩减代码的长度。

闭包表达式语法有如下一般形式:

{(parameters) -> returnType in
	statements
}

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

一般类型

下面的例子展示了之前backwords函数对应的闭包表达式形式的代码:

result = sorted(numbers,{(num1: Int, num2: Int) -> Bool in return num1 > num2})

根据上下文推断类型

因为排序闭包函数是作为sorted函数的参数传入的,Swift可以推断出其参数和返回值的类型。所以上面的表达式还可以这样写:

result = sorted(numbers,{num1, num2 in return num1 > num2})

单表达式闭包隐式返回

如果表达式只有一行的话,那么关键字return就可以不写,所以上面的表达式也可以这样写:

result = sorted(numbers,{num1, num2 in num1 > num2})

参数名称缩写

Swift 自动为内联函数提供了参数名称缩写的功能,可以直接用$0,$1,$2来顺序调用闭包的参数。如果使用了参数名称缩写,那么连参数的定义也可以省略。关键字in同样也可以省略,所以可以得到这样的代码:

//$0 和 $1 表示闭包中第一个和第二个Int类型的参数
result = sorted(numbers,{$0 > $1})

运算符缩写

如果你是个巨懒无比的程序员,那么这个杀手级的属性适合你。你只需传入一个大于号>就可以了。Swift会自动推断出你的大于号所想实现的功能。

result = sorted(numbers, >)

尾随闭包

如果您需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。 尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

func someFunctionThatTakesAClosure(closure: () -> ()) {
	// 函数体部分
}

// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
    // 闭包主体部分
})

// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
	// 闭包主体部分
}

注意: 如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉。

所以上面的例子也可以这样写:

result = sorted(numbers){ $0 > $1}

当闭包非常的长以至于不能在一行中进行书写时,尾随闭包将会非常有用。举例来说,Swift的Array类型有一个map方法,其获取一个闭包表达式作为其唯一参数。 数组中的每一个元素调用一次该闭包函数,并返回该元素所映射的值(也可以是不同类型的值)。 具体的映射方式和返回值类型由闭包来指定。当提供给数组闭包函数后,map方法将返回一个新的数组,数组中包含了与原数组一一对应的映射后的值。

var numbers = [1, 2, 3, 5, 4]
numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
})

let result = numbers.map
//此时的数组[3, 6, 9, 15, 12]

上例中尾随闭包语法在函数后整洁封装了具体的闭包功能,而不再需要将整个闭包包裹在map函数的括号内。

小结

以上的例子介绍了闭包所具有的特性和用法,并展示了代码量逐渐减少的过程。↓↓↓↓↓↓

func backwords(num1: Int, num2: Int) -> Bool{
return num1 > num2
}

var result = sorted(numbers,backwords)

result = sorted(numbers,{(num1: Int, num2: Int) -> Bool in return num1 > num2})

result = sorted(numbers,{num1, num2 in return num1 > num2})

result = sorted(numbers,{num1, num2 in num1 > num2})

result = sorted(numbers,{$0 > $1})

result = sorted(numbers, >)