闭包是自包含的功能块,可以传递并在代码中传递。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的 匿名函数比较相似。闭包可以从定义它们的上下文中捕获和存储对任何常量和变量的引用。这被称为对这些常量和变量的关闭。Swift为您处理捕获的所有内存管理。
函数中引用的全局函数和嵌套函数其实就是特殊的闭包。闭包的形式有:
1.全局函数:有名字但不能捕获任何值。
2.嵌套函数:有名字也可以从其封闭函数中捕获值。
3.闭包表达式:用轻量级语法编写的无名字的闭包,可以根据上下文环境捕获值。
Swift的闭包表达式具有干净、清晰的风格,其优化鼓励在常见场景中简洁、无杂乱的语法。这些优化包括:
1.从上下文推断参数和返回值类型
2.从单行表达式闭包的隐式返回(也就是闭包体只有一行代码,可以省略return)
3.可以简化参数名
4.提供了尾随闭包语法
闭包实例:
闭包表达式:
闭包表达式是一种用简短、集中的语法编写内联闭包的方式。闭包表达式提供了几种语法优化,用于以缩短的形式编写闭包,而不会失去清晰度或意图。下面的闭包表达式示例通过在多个迭代中完善sorted(by:)方法的单个示例来说明这些优化,每个迭代都以更简洁的方式表达相同的功能。Swift的标准库提供了一个名为sorted(by:)的方法,该方法根据您提供的排序闭包的输出对已知类型的值数组进行排序。完成排序过程后,sorted(by:)方法返回一个与旧数组具有相同类型和大小的新数组,其元素按正确的排序顺序排列。原始数组不会被sorted(by:)方法修改。sorted(by:)方法需要传入两个参数:
1.已知类型的数组
2.闭包函数,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回 true,反之返回 false。
由于排序闭包作为参数传递给方法,Swift可以推断其参数的类型及其返回的值的类型。当将闭包作为内联闭包表达式传递给函数或方法时,总是可以从上下文推断参数类型和返回类型。因此,当闭包用作函数或方法参数时,不需要以最完整的形式编写内联闭包。
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
单表达式闭包的隐式返回:单表达式闭包可以通过从其声明中省略return关键字来隐式返回其单表达式的结果,在这里,sorted(by:)方法参数的函数类型清楚地表明,闭包必须返回Bool值。由于闭包的主体包含返回Bool值的单个表达式(s1 > s2,因此没有歧义,并且可以省略return关键字。
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
参数名缩写:Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
reversedNames = names.sorted(by: { $0 > $1 } )
运算符函数:Swift 的String类型定义了关于大于号 (>) 的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。 而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift可以自动推断出您想使用大于号的字符串函数实现:
reversedNames = names.sorted(by: >)
尾随闭包:尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。函数调用可以包括多个尾随闭包。
reversedNames = names.sorted() { $0 > $1 }
如果函数只需要闭包表达式一个参数,当您使用尾随闭包时,您甚至可以把()省略掉。
reversedNames = names.sorted { $0 > $1 }
捕获值
闭包可以在其定义的上下文中捕获常量或变量。
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
闭包是引用类型,上面的例子中,incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值,这是因为函数和闭包都是引用类型。无论您将函数/闭包赋值给一个常量还是变量,您实际上都是将常量/变量的值设置为对应函数/闭包的引用。 上面的例子中,incrementByTen指向闭包的引用是一个常量,而并非闭包内容本身。这也意味着如果您将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包:
let alsoIncrementByTen = incrementByTen
// 返回的值也为40
print(alsoIncrementByTen())
闭包 可以类似 OC 里的 block 一样当作是一个对象,所以在最后一节“闭包是引用类型”中的例子,得到的一个闭包常量,这个闭包常量在内存里作为一个实例对象,此对象存储了捕获到的参数,调用三次就得到三个不同的结果