威立山

记录心路历程

0%

闭包概念

闭包是可以在你的代码中被传递和引用的功能性独立模块。Swift 中的闭包和 C 以及 Objective-C 中的 blocks 很像,还有其他语言中的匿名函数也类似。
在Swift中,提供三类闭包:

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

闭包表达式

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

闭包表达式语法的一般形式:

1
2
3
{ (parameters) -> (return type) in
statements
}
  • 关键字 in 来区分闭包的头和闭包函数体。

Swift 的标准库提供了一个叫做 sorted(by:) 的方法,会根据你提供的排序闭包将已知类型的数组的值进行排序。使用sorted方法对闭包简化
例子:

1
2
3
4
5
6
7
8
9
10
11
let names = ["Chris","Ewa","Barry","Daniella”]`
// 普通函数
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward)

// 使用闭包表达式
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})

类型推断(因排序闭包为实际参数来传递给函数,故 Swift 能推断它的形式参数类型和返回类型。)

1
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )

从单表达式闭包隐式返回,return 关键字能够被省略
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
简写的实际参数名
reversedNames = names.sorted(by: { $0 > $1 } )
还有一种更加简化的形式,直接使用操作符>,可以写出这样的前提是闭包定义刚好和操作符的定义吻合。
reversedNames = names.sorted(by: >)

逃逸和非逃逸闭包

Swift 的闭包分为 逃逸 与 非逃逸 两种。一个接受逃逸闭包作为参数的函数,逃逸闭包(可能)会在函数返回之后才被调用————也就是说,闭包逃离了函数的作用域。

逃逸闭包通常与异步控制流相关联,如下例所示:

  • 一个函数开启了一个后台任务后立即返回,然后通过一个完成回调(completion handler)报告后台任务的结果。
  • 一个视图类把『按钮点击事件执行的操作』封装成一个闭包,并存储为自身的属性。每次用户点击按钮时,都会调用该闭包。闭包会逃离属性的设置器(setter)。
  • 你使用 DispatchQueue.async 在派发队列(dispatch queue)上安排了一个异步执行的任务。这个闭包任务的生命周期会比 async 的作用域活得更长久。
    与之对应的 DispatchQueue.sync,它会一直等到任务闭包执行完毕后才返回——闭包永远不会逃逸。map 以及标准库中其他的序列和数组的算法也是非逃逸的。

为什么区分闭包的逃逸性和非逃逸性如此重要

简单来说,是为了管理内存。一个闭包会强引用它捕获的所有对象————如果你在闭包中访问了当前对象中的任意属性或实例方法,闭包会持有当前对象,因为这些方法和属性都隐性地携带了一个 self 参数。
这种方式很容易导致循环引用,这解释了为什么编译器会要求你在闭包中显式地写出对 self 的引用。这迫使你考虑潜在的循环引用,并使用捕获列表手动处理。
然而,使用非逃逸的闭包不会产生循环引用————编译器可以保证在函数返回时闭包会释放它捕获的所有对象。因此,编译器只要求在逃逸闭包中明确对 self 的强引用。显然,使用非逃逸闭包是一个更加愉悦的方案。

  • 从 Swift 3.0 开始,非逃逸闭包变成了闭包参数的默认形式。如果你想允许一个闭包参数逃逸,需要给这个类型增加一个 @escaping 的标注。

解决闭包循环引用

解决闭包的循环强引用
Swift 提供了一种优雅的方法来解决这个问题,称之为闭包捕获列表( closuer capture list )
捕获列表中的每一项都由 weak 或 unowned 关键字与类实例的引用(如 self )成对组成

1
2
3
4
lazy var someClosure: (Int, String) -> String = {
[unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
// closure body goes here
}

参考

闭包逃逸和非逃逸

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment