Swift閉包、閉包簡寫、尾隨閉包、逃逸閉包、自動閉包

閉包介紹

Swift閉包和Objective-C中的Block很類似,是一段自包含的函數代碼塊,可以在代碼中使用和傳遞。相當于一個匿名函數。
函數:是一個有名字的閉包。
普通函數:是一個有名字但不會捕獲任何值的閉包。
嵌套函數:是一個有名字并可以捕獲其封閉函數域內值的閉包。

閉包格式

閉包表達式語法有如下的一般形式:

{ (parameters) -> returnType in
    statements
}
閉包使用、尾隨閉包、閉包簡寫格式
// 以下閉包以Array的sorted函數為例
    func testClosure() {
        let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
        func backward(_ str1: String, _ str2: String) -> Bool {
            return str1 > str2
        }
        
        // sorted函數的參數是一個閉包,下面傳了一個方法名,由此說明:嵌套函數是一個有名字但并可以捕獲其封閉函數域內值的閉包
        var reversedNames = names.sorted(by: backward)
        
        // 普通閉包格式:(參數: 參數類型,...) -> 返回值類型 in ...
        reversedNames = names.sorted(by: {(_ str1: String, _ str2: String) -> Bool in return str1 > str2})
        
        // 根據Swift的類型推斷,參數類型及參數括號可以去掉,返回值類型可以去掉
        reversedNames = names.sorted(by: {str1, str2 in return str1 > str2})
       
        // 單行表達式:可以去掉return
        reversedNames = names.sorted(by: {str1, str2 in str1 > str2})
        
        // 使用參數名縮寫:參數和in也可以去掉
        reversedNames = names.sorted(by: {$0 > $1})
       
        // 使用運算符:因為Swift中為字符串重載了大于號小于號
        reversedNames = names.sorted(by: >)
        
        // 使用尾隨閉包:前提是閉包必須是函數的最后一個參數
        reversedNames = names.sorted() {$0 < $1}
        
        // 使用尾隨閉包:閉包是函數唯一參數時,可以省掉參數括號
        reversedNames = names.sorted {$0 < $1}
        
        print(reversedNames)
    }

閉包值捕獲,閉包是引用類型

先貼代碼:

// 值捕獲示例:下面嵌套函數incrementer本身沒有參數,它卻捕獲了外圍的參數runningTotal和amount
    func makeIncrementer(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementer() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementer
    }

// 調用
let makeIncrementByTen = makeIncrementer(forIncrement: 10)
        var ret = makeIncrementByTen()
        print("ret= \(ret)")// ret= 10
        ret = makeIncrementByTen()
        print("ret= \(ret)")// ret= 20
        ret = makeIncrementByTen()
        print("ret= \(ret)")// ret= 30
        
        let makeIncrementBySeven = makeIncrementer(forIncrement: 7)
        ret = makeIncrementBySeven()
        print("ret= \(ret)")// ret= 7
        
        ret = makeIncrementByTen()
        print("ret= \(ret)")// ret= 40
        
        // 上面的同一個閉包連續調用,值會遞增原因:閉包是引用類型,同一個閉包捕獲的變量并沒有釋放

逃逸閉包

逃逸閉包概念:當一個閉包作為參數傳到一個函數中,但是這個閉包在函數返回之后才被執行,我們稱該閉包從函數中逃逸。逃逸閉包多用來做函數回調,與Objective-C里的Block有異曲同工之妙。
代碼示例:

// 逃逸閉包示例:下面的閉包被方法外的數組引用,也就意味著,這個閉包在函數執行完后還可能被調用,所以必須使用逃逸閉包,不然編譯不過去
    var completionHandlers: [() -> Void] = []
    func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
        completionHandlers.append(completionHandler)
    }
// 非逃逸閉包
    func someFunctionWithNoneEscapingClosure(closure: () -> Void) {
        closure()
    }

    var x = 10
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib. 
        // 逃逸閉包
        someFunctionWithEscapingClosure {
            // 注意:逃逸閉包類必須顯式的引用self,
            self.x = 100
        }
        someFunctionWithNoneEscapingClosure {
            x = 200
        }
        print("x= \(x)")// x= 200
        completionHandlers.first?()
        print("x= \(x)")// x= 100
    }

自動閉包

自動閉包:閉包作為參數傳遞給函數時,可以將閉包定義為自動閉包(使用關鍵字@autoclosure)。這樣傳遞參數時,可以直接傳遞一段代碼(或者一個變量、表達式),系統會自動將這段代碼轉化成閉包。需要注意:過度使用 autoclosures 會讓你的代碼變得難以理解。這里不貼代碼了。

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

推薦閱讀更多精彩內容