閉包
閉包類似于 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)")
}