Swift Closure

闭包表达式

把一段代码块代码包起来,可以作为参数像变量那样传值。闭包可以读取到函数内部的变量,而且函数变量的作用域随闭包结束。

它会捕获闭包中使用的任何外部变量。它可以写出非常优雅的代码。有时它比使用 delegation 更好。

但闭包也容易引发循环依赖,产生内存死锁。

Swift 中闭包采取如下三种形式之一:

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

语法

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

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

闭包和函数

1
2
3
4
5
6
7
8
9
10
// 闭包,greetingPrinter 不是闭包的名字,它只是闭包包装起来后赋值给的变量名。
let greetingPrinter: () -> () = {
}
// 函数
func greetingPrinter() -> () = {
}
// 调用闭包,根函数也很相似
greetingPrinter()

Swift中对闭包的处理,首先处理成无名函数。

闭包作为参数

闭包可以作为一个参数使用,像传递静态值一样,可以传递一个动作(一段程序)。

1
2
3
4
5
fun repeat(count: Int, task: () -> ()) {
for i in 0..count {
task()
}
}

将这个程序块循环多少次。调用

1
2
3
repeat(10, {
println(“”)
})

{} 就是一个闭包(代码块)。

简化操作,隐式语法

Swift 的闭包表达式有很多简化的写法,水果公司也推荐这种写法:

  • 利用上下文推断参数和返回值类型
  • 隐式返回单表达式闭包,即单表达式闭包可以省略return关键字
  • 隐式参数:参数名称缩写
  • 依附式闭包语法

自动类型推导

参数类型和返回值,系统可以自动推导出类型,在使用时,可以不用再写类型。

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

隐式返回

如果闭包表达式,只有一行,可以不用写return。

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

隐式参数

如果变量只是用来声明参数名,无它用,闭包还可以用 $0$1 来表示。

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

依附式闭包 (Trailing Closures )

像上面的代码右括号跟着大括号被放到了第3行。这样写不大方便查找辨认。Swift中给出了一种解决办法。

如果一个函数的最后一个参数是闭包,那么它在语法上可以放在函数调用的外面,可以把小括号去掉(没有大括号包小括号,好看了点):

1
2
3
repeat(2) {
println(“")
}

如果闭包是最后一个参数,且只有一个参数,可以去掉小括号 ()

1
array.sort {$0 < $1 }

示例

Swift 标准库提供了sorted函数,会根据您提供的基于输出类型排序的闭包函数将已知类型数组中的值进行排序。 一旦排序完成,函数会返回一个与原数组大小相同的新数组,该数组中包含已经正确排序的同类型元素。

下面的闭包表达式示例使用sorted函数对一个String类型的数组进行字母逆序排序,以下是初始数组值:

1
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

sorted函数需要传入两个参数:

已知类型的数组
闭包函数,该闭包函数需要传入与数组类型相同的两个值,并返回一个布尔类型值来告诉sorted函数当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。
该例子对一个String类型的数组进行排序,因此排序闭包函数类型需为(String, String) -> Bool。

提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为sort函数的第二个参数传入:

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

如果第一个字符串 (s1) 大于第二个字符串 (s2),backwards函数返回true,表示在新的数组中s1应该出现在s2前。 对于字符串中的字符来说,“大于” 表示 “按照字母顺序较晚出现”。 这意味着字母”B”大于字母”A”,字符串”Tom”大于字符串”Tim”。 其将进行字母逆序排序,”Barry”将会排在”Alex”之前。

然而,这是一个相当冗长的方式,本质上只是写了一个单表达式函数 (a > b)。 在下面的例子中,利用闭合表达式语法可以更好的构造一个内联排序闭包。

函数式编程

函数式编程是一种理念,它通过让函数永远返回相同的类型,而实现不停的调用,单行表达式。

1
2
3
words.filter { $0.hasPrefix("hi") }
.map { $0.uppercaseString }
.reduce("ABCD") { "\($0) \($1)"}

REF::