Swift 閉包(block)詳解

1、閉包

Swift對閉包進行了簡化:

  • 利用上下文推斷參數和返回值類型
  • 隱式返回單表達式閉包,即單表達式閉包可以省略return關鍵字
  • 參數名稱縮寫
  • 尾隨(Trailing)閉包語法

先來看一個排序的例子,數組的降序排列

let usernames = ["Wangwu", "Lisi", "Xiaoming", "Zhangsan"]
func backWards(s1: String, s2: String) -> Bool
{
    return s1 > s2
}
let resultName1 = usernames.sorted(by: backWards)
//resultName1: ["Zhangsan", "Xiaoming", "Wangwu", "Lisi"]

 1.1 閉包表達式語法

{ (parameters) -> returnType in
      statements
}

1.2 單表達式閉包隱式返回

單行表達式閉包可以通過省略return關鍵字來隱式返回單行表達式的結果

let resultName2 = usernames.sorted { s1, s2 in s1 > s2 }

1.3 參數名稱縮寫

let resultName3 = usernames.sorted { $0 > $1 }

1.4 函數式閉包 

let resultName4 = usernames.sorted(by: >)

2. 捕獲值(Capturing Values)

閉包可以在其被定義的上下文中捕獲常量或變量。即使定義這些常量和變量的原作用域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。

3.閉包是引用類型(Closures Are Reference Types)

和類一樣,必要也是引用類型

4. 尾隨閉包(Trailing Closures))

尾隨閉包是一個書寫在函數括號之後的閉包表達式,函數支持將其作爲最後一個參數調用:

        let numReult2 = caculateTwoNumbers(num1: 3, num2: 4) {  $0 * $1 }
        print(numReult2)
    
    func caculateTwoNumbers(num1: Int, num2: Int, CaluFunction: (Int, Int) -> Int) -> Int{
        return CaluFunction(num1, num2)
    }

5. 逃逸閉包(@escaping)

    func mainFunc(){
        //調用函數
        doSomething(paramClosure: {print("hello")})
        doSomething(paramClosure:{print("word!")})
        //逃逸調用閉包
        for closurePrama in functionArray {
            closurePrama()
        }
        //非逃逸閉包
        someFunctionWithNonescapingClosure { (a) in
            print(a)
        }
    }
    //聲明一個存放函數的數組
    var functionArray: [() -> Void] = []
    //定義一個接收閉包參數的函數,如果定義非逃逸函數 func doSomething(@noescape paramClosure:() -> Void) 就會編譯錯誤
    func doSomething(paramClosure:@escaping  () -> Void){
        //把參數放入數組中,用於逃逸調用
        functionArray.append(paramClosure)
    }
    //非逃逸閉包 默認@noescape 可以省略不寫
    func someFunctionWithNonescapingClosure(closure: (_ num:Int) -> Void) {
        let a = 1
        closure(a)
    }

6、noescape是非逃逸的意思。

@noescape關鍵字代碼中扮演了一個標註的作用:來說明一個閉包參數,該閉包參數與此API是同步的,它只在此API中被調用。只要該API運行結束,該閉包的生命週期就結束。也就是說,該閉包逃不出該API的手掌心。哈哈哈哈!它對編譯器和API調用者來說:編譯器會對代碼做一些優化,而API調用者則可以放心大膽的使用該API,不用因爲擔心造成引用循環而去使用捕獲列表。同時在其中調用實例變量或實例方法的時候可以不使用"self."

      但是!如何使用這個@noescape標註,這是需要正確的姿勢的!

      上面的論述,只有在閉包是臨時創建,即沒有被API外部的任何其他屬性或全局變量持有的前提下才成立!!

func withLock(@noescape perform closure: () -> Void) {
    myLock.lock()
    closure()
    myLock.unlock()
}

In  Objective-C

- (void)performWithLock:(__attribute__((noescape)) void (^)())block {  // exposed as @noescape to Swift
    [myLock lock];
    block();
    [myLock unlock];
}

面試題:調用Masonry的block爲何不用weak?

原因就是使用了棧block,都是用NS_NOESCAPE修飾block.編譯器會相應地做一些優化,例如去掉一些多餘的對self的捕獲、retain、release操作。

- (NSArray *)mas_makeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_updateConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;
- (NSArray *)mas_remakeConstraints:(void(NS_NOESCAPE ^)(MASConstraintMaker *make))block;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章