Swift 之閉包

閉包

閉包類似于 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)")
        }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容