闭包和闭包表达式
闭包的概念
- 闭包是可以在你的代码中被传递和引用的功能性独立代码块
- 闭包能够捕获和存储定义在其上下文中的任何常量和变量的引用,这也就是所谓的闭包并包裹那些常量和变量,因为被称为“闭包”,Swift能够为你处理所有关于捕获的内存管理的操作
- 在函数章节中有介绍的全局和内嵌函数,实际上是特殊的闭包。闭包符合如下三种形式中的一种:
- 全局函数是一个有名字但不会捕获任何值的闭包
- 内嵌函数是一个有名字且能从其上层函数捕获值得闭包
- 闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量或变量值得没有名字的闭包
闭包表达式
- 闭包表达式是一种在简短行内就能写完闭包的语法
闭包表达式-从sorted函数说起
- Swift的标准库提供了一个叫做sorted(by:)的方法,会根据你提供的排序闭包将已知类型的数组的值进行排序。一旦它排序完成,sorted(by:)方法会返回与原数组类型大小完全相同的一个新数组,该数组的元素是已排序好的。原始数组不会被sorted(by:)方法修改
闭包表达式语法
- 闭包表达式语法能够使用常量形式参数、变量形式参数和输入输出形式参数,但不能提供默认值。可变形式参数也能使用,但需要在形式参数列表的最后面使用。元组也可被用来作为形式参数和返回类型
闭包表达式语法版本的backward
- 将之前backward(_:_:)函数改为闭包表达式版本
从语境中推断类型
- 因排序闭包为实际参数来传递给函数,故Swift能推断它的形式参数类型和返回类型
- sorted(by:)方法期望它的形式参数是一个(String, String) -> Bool类型的函数。这意味着(String, String)和Bool类型不需要被写成闭包表达式定义中的一部分,因为所有类型都能被推断,返回箭头(->)和围绕在形式参数名周围的括号也能被省略
从单表达式闭包隐式返回
- 单表达式闭包能够通过从它们的声明中删掉return关键字来隐式返回它们单个表达式的结果
简写实际参数名
- Swift自动对行内闭包提供简写实际参数名,可以通过0,1, $2等名字来引用闭包的实际参数值
运算符函数
- Swift的String类型定义了关于大于号(>)的特定字符串实现,让其作为一个有两个String类型形式参数的函数并返回一个Bool类型的值。这正好与sorted(by:)方法的形式参数需要的函数相匹配。因为,你能简单地传递一个大于号,并且Swift将推断你想使用大于号特殊字符串函数实现
尾随闭包
- 如果你需要将一个很长的闭包表达式作为函数最后一个实际参数传递给函数,使用尾随闭包将增强函数的可读性。尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式
闭包如何捕获值
捕获值
- 一个闭包能够从上下文捕获已被定义的常量和变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值
- 作为一种优化,如果一个值没有改变或者在闭包的外面,Swift可能会使用这个值得拷贝而不是捕获
- Swift也处理了变量的内存管理操作,当变量不再需要时会被释放
- 如果你建立了第二个incrementer,它将会有一个新的、独立runningTotal变量的引用
闭包是引用类型
- 在Swift中,函数和闭包都是引用类型
- 无论你什么时候赋值一个函数或者闭包给常量或者变量,你实际上都是将常量和变量设置为对函数和闭包的引用
- 如果你分配了一个闭包给类实例的属性,并且闭包通过引用该实例或者它的成员来捕获实例,你将在闭包和实例间会产生循环引用
逃逸闭包和自动闭包
逃逸闭包
- 当闭包作为一个实际参数传递给一个函数的时候,并且它会在函数返回之后调用,我们就说这个闭包逃逸了。当你声明一个接受闭包作为形式参数的函数时,你可以在形式参数前写@escaping来明确闭包是允许逃逸的
- 闭包可以逃逸的一种方法是被储存在定义与函数外的变量里。比如说,很多函数接受闭包实际参数来作为启动异步任务的回调。函数在启动任务后返回,但是闭包要直到任务完成--闭包需要逃逸,以便于稍后调用
- 让闭包@escaping意味着你必须在闭包中显式地引用self
自动闭包
- 自动闭包是一种自动创建的用来把作为实际参数传递给函数的表达式打包的闭包。它不接收任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值
- 这个语法的好处在于通过写普通表达式代替显示闭包而使你省略包围函数形式参数的括号
自动闭包允许你延迟处理,因为闭包内部的代码直到你调用它的时候才会运行。对于有副作用或者占用资源的代码来说很有用,因为它可以允许你控制代码何时才进行求值
- 当你传一个闭包作为实际参数到函数的时候,你会得到与延迟处理相同的行为
通过@autoclosure标志标记它的形式参数使用了自动闭包。现在你可以调用函数就像它接受了一个String实际参数而不是闭包。实际参数自动地转换为闭包,因为customerProvider形式参数的类型被标记为@autoclosure标记
自动+逃逸
- 如果你想要自动闭包允许逃逸,就同时使用@autoclosure和@escaping标志