1. 程式人生 > >Swift學習筆記之閉包

Swift學習筆記之閉包

pps eap animate nbsp ssi apps arr 全局 mef

簡介 (真的很簡)

閉包的完整形態是這個樣子的:

    { (parameters) -> returnType in
        statements
    }

寫在一行裏就是這樣:

    {(parameters) -> (returnType) in statements}

形式

閉包以三種形式存在:

1.全局的函數都是閉包,它們有自己的名字,但是沒有捕獲任何值。
2.內嵌的函數都是閉包,它們有自己的名字,而且從包含他們的函數裏捕獲值。
3.閉包表達式都是閉包,它們沒有自己的名字,通過輕量級的語法定義並且可以從上下文中捕獲值。

捕獲值

閉包可以捕獲上下文的值,然後把它存儲下來。至於存儲的是引用還是拷貝,Swift 會決定捕獲引用還是拷貝值,也會處理變量的內存管理操作。

下面這個例子可以說明很多問題:

    func makeIncrementor(forIncrement amount: Int) -> () -> Int {
        var runningTotal = 0
        func incrementor() -> Int {
            runningTotal += amount
            return runningTotal
        }
        return incrementor
    }

    let incrementByTen = makeIncrementor(forIncrement: 10)
    incrementByTen()    // runningTotal = 10
    incrementByTen()    // runningTotal = 20
    incrementByTen()    // runningTotal = 30

    let incrementByTen2 = makeIncrementor(forIncrement: 10)
    incrementByTen2()    // runningTotal = 10

    let incrementByTen3 = incrementByTen
    incrementByTen()    // runningTotal = 40

因為 incrementByTen2 聲明了一個全新的閉包,所以 runningTotal 並沒有繼續接著上面的計數。而 incrementByTen3incrementByTen 指向的是同一個閉包,所以 runningTotal 的值是累加的。

參數縮寫

我們可以直接用 $0 $1 $2 這種來依次定義閉包的參數。比如 sorted 函數:

    var reversed = sorted(["c","a","d","b"], { $0 > $1 })   // d c b a

尾隨閉包

我一直覺得閉包最後這個 }) 很難看,在 JS 中隨處可見這種情況。如果閉包是函數的最後一個參數,Swift 提供了尾隨閉包 (Trailing Closures) 解決這個審美問題:

    // 以下是不使用尾隨閉包進行函數調用
    someFunc({
        // 閉包主體部分
    })

    // 以下是使用尾隨閉包進行函數調用
    someFunc() {
      // 閉包主體部分
    }

OK那麽前面那個排序的可以用尾隨閉包這麽改寫:

    var reversed = sorted(["c","a","d","b"]) { $0 > $1 } // d c b a

如果除了閉包沒有其他參數了,甚至可以把小括號也去掉。

還記得我們前面寫的 mapreducefilter 三元大將嗎?用尾隨閉包可以讓它們變得更好看。比如前面那個選出大於30的數字的 filter 就可以這樣寫:

    var oldArray = [10,20,45,32]
    var filteredArray  = oldArray.filter{
        return $0 > 30
    }

    println(filteredArray) // [45, 32]

變形

變形金剛神馬的最有愛了。總結一下 closure 的變形大致有以下幾種形態:

    [1, 2, 3].map( { (i: Int) ->Int in return i * 2 } )
    [1, 2, 3].map( { i in return i * 2 } )
    [1, 2, 3].map( { i in i * 2 } )
    [1, 2, 3].map( { $0 * 2 } )
    [1, 2, 3].map() { $0 * 2 }
    [1, 2, 3].map { $0 * 2 }

對比

通過 UIView 的 animateWithDuration 方法對 block 和 closure 進行簡單的對比。

block 版本:

    [UIView animateWithDuration:1 animations:^{
        // DO SOMETHING
    } completion:^(BOOL finished) {
        NSLog(@"OVER");
    }];

closure 版本:

    UIView.animateWithDuration(1, animations: { () in 
        // DO SOMETHING
        }, completion:{(Bool)  in
            println("OVER")
        })

可以看到原來的 ^ 符號已經不復存在,取而代之的是加在參數和返回值後面的 in 。註意,如果有 in 的話,就算沒有參數沒有返回值也一定需要 () ,否則會報錯。

總結

和 Objective-C 的 FuckingBlock 一樣,Swift 出來之後 FuckingClosure 也就應運而生。總結了 Closure 的常用語法和格式:

    // 作為變量
    var closureName: (parameterTypes) -> (returnType)

    // 作為可選類型的變量
    var closureName: ((parameterTypes) -> (returnType))?

    // 做為一個別名
    typealias closureType = (parameterTypes) -> (returnType)

    // 作為函數的參數
    func({(parameterTypes) -> (returnType) in statements})

    // 作為函數的參數
    array.sort({ (item1: Int, item2: Int) -> Bool in return item1 < item2 })

    // 作為函數的參數 - 隱含參數類型
    array.sort({ (item1, item2) -> Bool in return item1 < item2 })

    // 作為函數的參數 - 隱含返回類型
    array.sort({ (item1, item2) in return item1 < item2 })

    // 作為函數的參數 - 尾隨閉包
    array.sort { (item1, item2) in return item1 < item2 }

    // 作為函數的參數 - 通過數字表示參數
    array.sort { return $0 < $1 }

    // 作為函數的參數 - 尾隨閉包且隱含返回類型
    array.sort { $0 < $1 }

    // 作為函數的參數 - 引用已存在的函數
    array.sort(<)

References

  • Closures
  • Closure Expressions in Swift
  • Fucking Closure
  • Writing completion blocks with closures in Swift
  • Enough About Swift Closures to Choke a Horse
  • Functions and Closures in Swift
  • Swift How-To: Writing Trailing Closures

  知識來源:http://lib.csdn.net/article/swift/474

Swift學習筆記之閉包