天天看点

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

闭包表达式(Closure Expression)

可以通过func定义一个函数,也可以通过闭包表达式定义一个函数

  • func函数
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包
  • 闭包定义函数
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

闭包表达式调用可以直接省略参数名

闭包定义函数也可以写为:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包
  • 闭包表达式的结构
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

闭包表达式的简写

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

尾随闭包

如果将一个很长的闭包表达式作为函数的最后一个实参,使用尾随闭包可以增强函数的可读性 

尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

上面的函数使用闭包调用:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

如果闭包表达式是函数的唯一实参,而且使用了尾随闭包的语法,那就不需要在函数名后边写圆括号

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

针对上面的函数,一般与闭包调用的对比:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

示例 - 数组的排序

数组排序的底层代码的定义:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

由上面的底层代码可知,想要自定义一个数组排序,只需要传入一个有两个参数,返回值是Bool的函数

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

自定义排序方法的多种调用方式(闭包和简写):

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

闭包(Closure)

 网上有各种关于闭包的定义,个人觉得比较严谨的定义是 :

  • 一个函数和它所捕获的变量\常量环境组合起来,称为闭包 
  • 一般指定义在函数内部的函数
  •  一般它捕获的是外层函数的局部变量\常量

定义了一个闭包:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

注:return plus前分配了一段堆空间,将num的值存储到了这个堆空间,调用plus访问的num实际上是堆空间的num, plus方法实际接收了两个参数:i和堆空间地址

使用反汇编看它实现的底层汇编代码:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

由图上红色框标注的汇编代码我们可以看出,在闭包里调用了swift_allocObject方法,我们可以理解为它在堆区申请了一块内存空间,用来存储它捕获的外层函数的局部变量或常量也就是num,这也就决定了num不会立刻销毁,由此我们可以看下它的计算结果:

func getFn() -> Fn {
    var num = 0
    func plus(_ i: Int) -> Int{
        num += 1
        return num
    }
    return plus
}

var fn = getFn()
print(fn(1))  //结果为1
print(fn(2))  //结果为3
print(fn(3))  //结果为6
print(fn(4))  //结果为10
           

可以看出计算结果是累加的,那就因为num存储在堆空间,没有销毁,上面的计算是对num的不断累加。

注意:如果num是全局变量,则不会在堆空间开辟内存。

注意:每调用一次getFn()都会申请一个新的内存空间,举例如下:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

由上面的计算可以看出,fn1和fn2是分开独立累加的,也就是fn1和fn2分别开辟了一块新内存,互不影响

反汇编代码如下:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

由上面的反汇编代码可以看出fn1和fn2前8个字节相同,因为都是指向同一个plus方法,但是后8个字节不同,因为分配的堆空间不同。

  •  可以把闭包想象成是一个类的实例对象
  •  内存在堆空间
  • 捕获的局部变量\常量就是对象的成员(存储属性) 
  • 组成闭包的函数就是类内部定义的方法
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

闭包举例

  • 第一个例子:
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

上面的闭包可以看成一个类:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包
  • 第二个例子:
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

上面的闭包同样可以看成一个类:

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

参数注意

如果返回值类型是函数类型,那么参数的修饰要保持统一

Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

自动闭包

  •  @autoclosure 会自动将 20 封装成闭包 { 20 }
  •  @autoclosure 只支持 () -> T 格式的参数
  • @autoclosure 并非只支持最后1个参数
  •  空合并运算符 ?? 使用了 @autoclosure 技术
  •  有@autoclosure、无@autoclosure,构成了函数重载
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包
Swift学习(八):闭包(闭包表达式,尾随闭包,闭包的定义,自动闭包)闭包表达式(Closure Expression)闭包表达式的简写尾随闭包示例 - 数组的排序闭包(Closure)闭包举例参数注意自动闭包

为了避免与期望冲突,使用了@autoclosure的地方最好明确注释清楚:这个值会被推迟执行。