闭包
闭包类似于 OC 的 block,但是比 OC 的 block 应用面更广
- 在 OC 中 block 是匿名的函数
- 在 Swift 中函数是特殊的闭包
闭包的应用场景
- 异步执行完成回调
- 控制器间回调
- 自定义视图回调
回调特点
- 以参数回调处理结果
- 返回值为 Void
代码
- 定义一个函数
//: 定义一个 sum 函数
func sum(x: Int, y: Int) -> Int {
return x + y
}
// 定义一个指向函数的常量
// 在 Swift 中函数本身就可以当作参数被定义和传递
let sumFunc = sum
// 执行函数常量
print(sumFunc(10, 30))
- 闭包的四步演练
// 1> 最简单的闭包
let myBlock1 = {
print("hello block")
}
myBlock1()
// 2> 闭包的完整写法,闭包的所有内容都包含在 {} 中
// `in` 用于区分闭包的参数和实现代码
let myBlock2 = { () -> () in
print("hello myBlock2")
}
myBlock2()
// 3> 带参数的闭包
let myBlock3 = { (x: Int, y: Int) -> () in
print(x + y)
}
myBlock3(10, 20)
// 4> 带返回值的闭包
let myBlock4 = { (x: Int, y: Int) -> Int in
return x + y
}
print(myBlock4(10, 50))
GCD
- 异步加载数据
/// GCD 模拟异步加载数据
func loadData() -> () {
// GCD 概念:将任务添加到队列,并且指定任务的执行函数
// 翻译 -> `队列` 调度 `任务` 以 `同步/异步` 方式执行
DispatchQueue.global().async {
print("异步耗时加载数据 \(Thread.current)")
let json = ["新闻1", "新闻2", "出大事了"]
DispatchQueue.main.async(execute: {
print("主线程回调 \(Thread.current) \(json)")
})
}
print("come here")
}
添加回调闭包
/// GCD 模拟异步加载数据
func loadData(completion: @escaping ([String])->()) -> () {
// GCD 概念:将任务添加到队列,并且指定任务的执行函数
// 翻译 -> `队列` 调度 `任务` 以 `同步/异步` 方式执行
DispatchQueue.global().async {
print("异步耗时加载数据 \(Thread.current)")
let json = ["新闻1", "新闻2", "出大事了"]
DispatchQueue.main.async(execute: {
print("主线程回调 \(Thread.current) \(json)")
completion(json)
})
}
print("come here")
}
尾随闭包
- 尾随闭包,如果闭包是最后一个参数,可以提前结束函数,最后一个参数直接使用 {} 闭包的方式传递
// 尾随闭包写法
// - 如果闭包是最后一个参数,函数可以提前结束,并且在末位直接追加 `{}` 闭包
// - 尾随闭包可以省略参数名
loadData { (result) in
print("--- \(result)")
}
// 函数参数的写法
loadData(completion: {result in
print("--- \(result)")
})
闭包的循环引用
[weak self]
表示闭包中的 self 是弱引用,如果 self 被释放,会自动设置为 nil
与 OC 的 __weak 等效
[unowned self]
表示闭包中的 self 是 assign 的,如果 self 被释放,指针地址保持不变,会出现野指针的错误
与 OC 的 __unsafe_unretained 等效
class ViewController: UIViewController {
var finishedCallBack: (()->())?
override func viewDidLoad() {
super.viewDidLoad()
loadData {
// 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
print("OK \(self)")
}
}
func loadData(completion: ()->()) -> () {
// 使用属性记录闭包
finishedCallBack = completion
DispatchQueue.global().async {
print("耗时操作")
Thread.sleep(forTimeInterval: 1.0)
DispatchQueue.main.async {
print("主线程回调")
// 闭包是提前准备好的代码,在执行时需要指定上下文的语境,因此需要 `self.`
// 使用 `?` 表示一旦闭包不存在就什么都不做
// 使用 `!` 表示无论闭包是否存在都执行,如果真的不存在,会崩溃
self.finishedCallBack?()
}
}
}
// 析构函数 - 类似于 OC 的 dealloc
deinit {
print(#function)
}
}
解除循环引用
- 一
// 1. 方法一,类似于 OC 的方法
// * weak 修饰的变量有可能在运行时被修改为 nil,因此不能使用 let
weak var weakSelf = self
loadData {
// 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
print("OK \(weakSelf)")
}
- 二
// 2. 方法二,Swift 的写法
// [weak self] 表示闭包中的 self 是弱引用,如果 self 被释放,会自动设置为 nil
// 与 OC 的 `__weak` 等效
loadData { [weak self] in
// 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
print("OK \(self?.view)")
}
- 三
// 3. 方法三
// [unowned self] 表示闭包中的 self 是 assign 的,如果 self 被释放,指针地址保持不变
// 会出现野指针的错误
// 与 OC 的 `__unsafe_unretained` 等效
loadData { [unowned self] in
// 与 OC 的 block 一样,闭包同样会对外部变量做一次 copy(强引用)
print("OK \(self.view)")
}