文章目录
  1. 1. 1、闭包是什么?
  2. 2. 2、闭包的各种用法
  3. 3. 3、Swift中的闭包

1、闭包是什么?

官方文档中,闭包的定义比较难以理解。只是说跟Objective-C中的block和其他语言当中的lambda类似。我的理解,在Swift中,首先,闭包是一个代码块,可以匿名,也可以有名字;其次,闭包需要在一个确定的代码上下文中使用,能够捕获上下文的变量,使用这些变量来处理一些额外的逻辑;第三,闭包有很多形式,可以根据上下文来写出非常简要的闭包形式,使得代码更加优雅。

在Swift中,提供三类闭包:

  • 全局函数是一个有名字但不会捕获任何值的闭包。
  • 嵌套函数是一个有名字并可以捕获到其封闭函数域内的值的闭包。
  • 闭包表达式是一个利用轻量级语法所写的,可以捕获其上下文中变量或常量值的匿名闭包。

其中前2种闭包都好理解,函数大家都很熟悉,全局函数其实用的很少,一般都会有类的封装,而嵌套函数在实际用途中也很少,还不如直接用匿名闭包的表达更加清晰。所以Swift中对于闭包的使用,基本都是在说对匿名闭包的使用。

2、闭包的各种用法

闭包表达式最基本的形式如下:

1
2
3
{ (parameters) -> returnType in
statements
}

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

我们来看看官方文档举的例子:sort方法。sort方法的原型如下:

1
2
3
Array.sort(isOrderedBefore: (Self.Generator.Element, Self.Generator.Element) -> Bool)
注意:Swift1.2取消了sorted函数的支持。以后都使用数组对应的sort方法。

sort方法需要传入一个参数:

  • 闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉
    sorted函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。
    如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。

sort方法使用如下:

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

可以看到,函数backwards符合sort方法参数闭包函数的类型要求。返回的bool型用于排序,这里表示s1会排在s2前面,也就是按照字母倒序排列数组中的元素。

也可以直接使用匿名函数来传入到sort方法中:

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

以上也可以直接写成一行,这是闭包最完整的书写。也提供以下简写的方法:

  • 类型推断
    可以根据上下文,把闭包函数中的参数类型省去。比如上列中,names是String数组,可以推断出sort函数中的s1和s2必须是字符串类型,所以可以省去类型:
1
reversed = names.sort( { s1, s2 in return s1 > s2 } )
  • 省去return
    如果是单个表达式,可以根据闭包定义推断出返回类型,比如上列中闭包返回的Bool,所以可以省去return:
1
reversed = names.sort( { s1, s2 in s1 > s2 } )
  • 省去参数
    甚至可以省去参数,直接使用$0,$1来代替s1,s2:
1
reversed = names.sort( { $0 > $1 } )

还有一种更加简化的形式,直接使用操作符>,可以写出这样的前提是闭包定义刚好和操作符的定义吻合。

1
reversed = names.sort(>)
  • 尾随闭包(Trailing Closures)
    如果闭包在函数定义中是最后一个参数,可以把()中的闭包定义{…}放到外面来,这样看起来更加舒服:
1
reversed = names.sort() { s1, s2 in s1 > s2 }

尾随闭包在很多地方都会用到,是一个很重要的特性。下一节会看到更多应用。

3、Swift中的闭包

数组的map ,reduce, filter
除了sort方法以外,数组还有一些高阶方法:map,filter,reduce。在Functional Programming in Swift第4章中,详细介绍了map,filter,reduce方法的原理。cocoachina上也有如何使用的介绍。这里也做一下简要的笔记。看看如何使用这些方法。

  • map方法

map方法作用是把数组[T]通过闭包函数把每一个数组中的元素变成U类型的值,最后组成数组[U]
原型如下:

1
func map(transform: (T) -> U) -> [U]

Functional Programming in Swift中,实现了map函数如下:

1
2
3
4
5
6
func map<T, U>(xs: [T], f: T -> U) -> [U]
{
var result: [U] = [] for x in xs
{ result.append(f(x))
} return result
}

可以运用尾随闭包来调用此函数:

1
2
3
4
5
6
7
let xs: [Int] = [1, 2, 3]
let result = map(xs){x in x * 2}
//result = [2, 4, 6]
//也可以生成字符串数组:
let strs = map(xs){x in "number:\(x)" }
//strs = ["number:1", "number:2", "number:3"]
  • filter方法
    filter方法作用是根据闭包函数返回的Bool值来过滤值。为True则加入到结果数组中。
    原型如下:
1
filter(includeElement: (T) -> Bool) -> [T]

类似的实现函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let exampleFiles = ["README.md",
"Hello.swift",
"Swift.swift",
"FlappyBird.swift"]
func filter<T>(xs: [T], check: T -> Bool) -> [T]
{
var result: [T] = []
for x in xs
{
if check(x)
{
result.append(x)
}
}
return result
}
var result = filter(exampleFiles){ file in file.hasSuffix("swift") }
//result = ["Hello.swift", "Swift.swift", "FlappyBird.swift"]
  • reduce方法

原型如下:

1
reduce(initial: U, combine: (U, T) -> U) -> U

reduce的作用给定一个类型为U的初始值,把数组[T]中每一个元素传入到combine的闭包函数里面,通过计算得到最终类型为U的结果值。

reduce的类似实现是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func reduce<A, R>(arr: [A], _ initialValue: R, combine: (R, A) -> R) -> R
{
var result = initialValue
for i in arr
{
result = combine(result, i)
}
return result
}
let input = [1, 2, 3, 4]
var sum = reduce(input, 0){ x, y in x + y }
//sum = 10
* 这里要注意的是,Swift2.0开始,统一了函数和方法的参数定义方式,都以方法的为准,就是从第二个参数开始,默认外部名称和内部名称相同。所以为了调用的统一,在initialValue前加了_,表示忽略外部名称。

reduce函数还可以用来实现map函数和filter函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func mapUsingReduce<T, U>(xs: [T], f: T -> U) -> [U]
{
return reduce(xs, []){ result, x in result + [f(x)] }
}
var result = mapUsingReduce(input){ x in x * 3 }
result1
func filterUsingReduce<T>(xs: [T], check: T -> Bool) -> [T]
{
return reduce1(xs, [])
{
result , x in return check(x) ? result + [x] : result
}
}
result = filterUsingReduce(exampleFiles)
{
file in file.hasSuffix("swift")
}

参考资料:

文章目录
  1. 1. 1、闭包是什么?
  2. 2. 2、闭包的各种用法
  3. 3. 3、Swift中的闭包