swift中的闭包
闭包是自包含的函数代码块,可以在代码中被传递和使用。swift中的闭包与C和Objective-C中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。
闭包可以捕获和存储其所在的上下文中任意常量和变量的引用。被称为包裹常量和变量。Swift会管理在捕获过程中涉及到的所有内存操作。
闭包采取如下三种形式之一:
- 全局函数是一个有名字但不会捕获任何值的闭包
- 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包。
- 闭包表达式是一个利用轻量级语法缩写的可以捕获其上下文中变量或者常量值的匿名闭包
Swift的闭包表达式拥有简洁的风格,其特点如下
- 利用上下文推断函数和返回值类型
- 隐式返回单表达式闭包,即单表达式闭包可以省略 return 关键字
- 参数名称缩写
- 尾随闭包语法
嵌套函数是一个在较复杂函数中方便进行命名和定义自包含代码块的方式。当然,有时候编写小巧的没有完整定义和命名的类函数结构也是很有用处的,尤其是在处理一些函数并需要将另外一些函数作为该函数的参数时。
闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单明了。下面闭包表达式的例子通过使用几次迭代展示了sorted(by:)方法定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能。
sorted 方法
swift标准库提供了名为 sorted(by:)的方法,它会根据所提供的用于排序的闭包函数将已知类型数组中的值进行排序。一旦排序完成,sorted(by:)方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:)方法修改。
sorted(by:)方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回true,反之返回false。
提供排序闭包函数的一种方式是撰写一个符合其类型要求的普通函数,并将其作为sorted(by:)方法的参数传入:
func backward(_ s1:String,_s2:String)->Bool) { return s1>s2 }
闭包表达式语法构建内联排序闭包
闭包表达式语法有如下的一般形式
{(parameters)-> returnType in
statements
}
闭包表达式参数可以是 in-out 参数,但不能设定默认值。也可以使用具名的可变参数。元组也可以作为参数和返回值。
下面的例子展示了之前 backward(::)函数对应的闭包表达式版本的代码:
reverseNames = names.sorted(by:{s1:String,s2:String}->Bool in)
return s1>s2
})
需要注意的是内联闭包参数和返回值类型声明与backward(::)函数类型声明相同。在这两种方式中,都可写成了(s1:String,s2:String)->Bool 然而在内联闭包表达式中,函数和返回值类型都写在大括号内,而不是大括号外。
闭包的函数体部分由关键字in 引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始。
由于这个闭包的函数体部分如此短,以至于可以将其改写成一行代码:
reversedNames = names.sorted(by:{(s1:String,s2:String)->Bool in return s1>s2})
该例中sorted(by:)方法的整体调用保持不变,一对圆括号仍然包裹住了方法的整个参数。然而参数现在变成了内联闭包。
根据上下文推断类型
因为排序闭包函数时作为sorted(by:)方法的参数传入的,swift可以推断其参数和返回值的类型.sorted(by:)方法被一个字符串数组调用,因此其参数必须是(String,String)->Bool 类型的函数。这意味着(String,string)和 Bool 类型并不需要作为闭包表达式定义的一部分。因为所有的类型都可以被准确推断,返回箭头和围绕在参数周围的括号也可以省略:
reversedNames = names.sorted(by:{s1,s2 in return s1>s2})
通过内联闭包表达式构造的闭包作为参数传递给函数或者方法时,总是能够推断出闭包的参数和返回值类型。这意味着闭包作为函数或者方法的参数时,几乎不需要利用完整格式构造内联闭包。
但是如果完整格式的闭包可以提高代码的可读性,我们更应该使用完整格式的闭包。而在sorted(by:)方法这个例子里,显然闭包的目的就是排序。
单表达式闭包隐式返回
单行表达式闭包可以通过省略 return 关键字来隐式返回单行表达式的结果,
参数名称缩写
swift自动为内联闭包提供了参数名称缩写功能,可以直接通过$0,$1,$2来顺序条用闭包的参数,一次类推。
如果在闭包表达式中使用参数名称缩写,可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。in 关键字也同样可以被省略,因为此时,闭包表达式完全由闭包函数体构成。
reversedNames = names.sorted(by:{$0>s1})
在这个例子中,$0和$1表示闭包中第一个和第二个String类型的参数。
运算符方法
swift的String类型定义了关于大于号的字符串实现,其作为一个函数接收两个String类型的参数并返回Bool类型的值。而这正好与sorted(by:)方法的参数需要的函数类型相符合。因此,你可以简单的传递一个大于号,swift可以自动的推断出你想使用大于号的字符串函数实现:
reversedNames = names.sorted(by:>)