swift學習筆記2——函數、閉包

以前學習swift時的我的筆記,根據github:the-swift-programming-language-in-chinese學習、總結,將重要的內容提取,加以理解後整理爲學習筆記,方便之後查詢用。詳細能夠參考the-swift-programming-language-in-chinese,或者蘋果官方英文版文檔html

當前版本是swift2.2ios

函數

func sayHello(personName: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return sayHelloAgain(personName)
    } else {
        return sayHello(personName)
    }
}

函數參數名稱(Function Parameter Names)

函數參數都有一個外部參數名(external parameter name)和一個局部參數名(local parameter name)。外部參數名用於在函數調用時標註傳遞給函數的參數,局部參數名在函數的實現內部使用。git

func someFunction(firstParameterName: Int, secondParameterName: Int) {
    // function body goes here
    // firstParameterName and secondParameterName refer to
    // the argument values for the first and second parameters
}
someFunction(1, secondParameterName: 2)

通常狀況下,第一個參數省略其外部參數名,第二個以及隨後的參數使用其局部參數名做爲外部參數名。全部參數必須有獨一無二的局部參數名。儘管多個參數能夠有相同的外部參數名,但不一樣的外部參數名能讓你的代碼更有可讀性。github

你能夠在局部參數名前指定外部參數名,中間以空格分隔:swift

func someFunction(externalParameterName localParameterName: Int) {
    // function body goes here, and can use localParameterName
    // to refer to the argument value for that parameter
}

若是你提供了外部參數名(包括局部參數名做爲外部參數名的狀況),那麼函數在被調用時,必須使用外部參數名。若是想忽略外部參數名,使用_替代便可api

默認參數值(Default Parameter Values)

你能夠在函數體中爲每一個參數定義默認值(Deafult Values)。當默認值被定義後,調用這個函數時能夠忽略這個參數。數組

func someFunction(parameterWithDefault: Int = 12) {
}
someFunction(6) // parameterWithDefault is 6
someFunction() // parameterWithDefault is 12

將帶有默認值的參數放在函數參數列表的最後。這樣能夠保證在函數調用時,非默認參數的順序是一致的,同時使得相同的函數在不一樣狀況下調用時顯得更爲清晰。不過不放在最後也是能夠的閉包

可變參數(Variadic Parameters)

一個可變參數(variadic parameter)能夠接受零個或多個值。函數調用時,你能夠用可變參數來指定函數參數能夠被傳入不肯定數量的輸入值。經過在變量類型名後面加入(...)的方式來定義可變參數。app

可變參數的傳入值在函數體中變爲此類型的一個數組。例如,一個叫作 numbers 的 Double... 型可變參數,在函數體內能夠當作一個叫 numbers 的 [Double] 型的數組常量。ide

// 求算數平均數
func arithmeticMean(numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)

// 使用AnyObject就相似print函數了
func alvalible(num:AnyObject...) {
    print(num[0],num[1])
    
}

常量參數和變量參數

函數參數默認是常量。試圖在函數體中更改參數值將會致使編譯錯誤。
經過在參數名前加關鍵字 var 來定義變量參數:

func alignRight(var astring: String) -> String {
    astring += "123"
    return astring
}

swift是值傳遞的,可變參數做用域只在函數內部,對調用者不產生影響,和C中的指針傳遞不同,若是想達到C指針的效果,可使用輸入輸出參數

輸入輸出參數

變量參數,正如上面所述,僅僅能在函數體內被更改。若是你想要一個函數能夠修改參數的值,而且想要在這些修改在函數調用結束後仍然存在,那麼就應該把這個參數定義爲輸入輸出參數(In-Out Parameters)。調用的時候實參必須可變,且加上&

func swapTwoInts(inout a: Int, inout _ b: Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)

輸入輸出參數不能有默認值,並且可變參數不能用 inout 標記。若是你用 inout 標記一個參數,這個參數不能被 var 或者 let 標記。

函數參數

func addTwoInts(a: Int, _ b: Int) -> Int {
    return a + b
}
func printHelloWorld() {
    print("hello, world")
}

第一個函數的類型是(Int, Int) -> Int,能夠解讀爲「這個函數類型有兩個 Int 型的參數並返回一個 Int 型的值。」。第二個() -> Void

在 Swift 中,使用函數類型就像使用其餘類型同樣。例如,你能夠定義一個類型爲函數的常量或變量,並將適當的函數賦值給它:

var mathFunction: (Int, Int) -> Int = addTwoInts

mathFunction(2, 3) // 直接使用

這個能夠解讀爲:

「定義一個叫作 mathFunction 的變量,類型是‘一個有兩個 Int 型的參數並返回一個 Int 型的值的函數’,並讓這個新變量指向 addTwoInts 函數」。

就像其餘類型同樣,當賦值一個函數給常量或變量時,你可讓 Swift 來推斷其函數類型:

let anotherMathFunction = addTwoInts

參數爲函數類型

func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \\(mathFunction(a, b))")
}

函數類型做爲返回類型

func stepForward(input: Int) -> Int {
    return input + 1
}
func stepBackward(input: Int) -> Int {
    return input - 1
}
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    return backwards ? stepBackward : stepForward
}

嵌套函數

嵌套函數(Nested Functions)
這章中你所見到的全部函數都叫全局函數(global functions),它們定義在全局域中。你也能夠把函數定義在別的函數體中,稱做嵌套函數(nested functions)。

默認狀況下,嵌套函數是對外界不可見的,可是能夠被它們的外圍函數(被嵌套的函數即例中的chooseStepFunction)調用。一個外圍函數也能夠返回它的某一個嵌套函數,使得這個函數能夠在其餘域中被使用。

你能夠用返回嵌套函數的方式重寫 chooseStepFunction(_:) 函數:

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backwards ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

閉包

閉包表達式語法有以下通常形式:

{ (parameters) -> returnType in
    statements
}

閉包表達式語法可使用常量、變量和inout類型做爲參數,不能提供默認值。也能夠在參數列表的最後使用可變參數。元組也能夠做爲參數和返回值。

sort(_:)方法接受一個閉包,該閉包函數須要傳入與數組元素類型相同的兩個值,並返回一個布爾類型值來代表當排序結束後傳入的第一個參數排在第二個參數前面仍是後面。若是第一個參數值出如今第二個參數值前面,排序閉包函數須要返回true,反之返回false。sort函數會將數組裏面的元素按照冒泡排序同樣的返回給s1,s2兩個參數,而後進行比較排序。

函數對應的閉包表達式版本的代碼:

reversed = names.sort({ (s1: String, s2: String) -> Bool in
    return s1 > s2
})

然而在內聯閉包表達式中,函數和返回值類型都寫在大括號內,而不是大括號外。
閉包的函數體部分由關鍵字in引入。該關鍵字表示閉包的參數和返回值類型定義已經完成,閉包函數體即將開始。

由於排序閉包函數是做爲sort(_:)方法的參數傳入的,Swift 能夠推斷其參數和返回值的類型。sort(_:)方法被一個字符串數組調用,所以其參數必須是(String, String) -> Bool類型的函數。這意味着(String, String)和Bool類型並不須要做爲閉包表達式定義的一部分。由於全部的類型均可以被正確推斷,返回箭頭(->)和圍繞在參數周圍的括號也能夠被省略:

reversed = names.sort( { s1, s2 in return s1 > s2 } )

實際上任何狀況下,經過內聯閉包表達式構造的閉包做爲參數傳遞給函數或方法時,均可以推斷出閉包的參數和返回值類型。 這意味着閉包做爲函數或者方法的參數時,您幾乎不須要利用完整格式構造內聯閉包。

單行表達式閉包能夠經過省略return關鍵字來隱式返回單行表達式的結果,如上版本的例子能夠改寫爲:

reversed = names.sort( { s1, s2 in s1 > s2 } )

在這個例子中,sort(_:)方法的第二個參數函數類型明確了閉包必須返回一個Bool類型值。由於閉包函數體只包含了一個單一表達式(s1 > s2),該表達式返回Bool類型值,所以這裏沒有歧義,return關鍵字能夠省略。

參數名稱縮寫

Swift 自動爲內聯閉包提供了參數名稱縮寫功能,您能夠直接經過$0,$1,$2來順序調用閉包的參數,以此類推。

尾隨閉包(Trailing Closures)

若是您須要將一個很長的閉包表達式做爲最後一個參數傳遞給函數,可使用尾隨閉包來加強函數的可讀性。尾隨閉包是一個書寫在函數括號以後的閉包表達式,函數支持將其做爲最後一個參數調用:

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // 函數體部分
}

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

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

在閉包表達式語法一節中做爲sort(_:)方法參數的字符串排序閉包能夠改寫爲:

reversed = names.sort() { $0 > $1 }

若是函數只須要閉包表達式一個參數,當您使用尾隨閉包時,您甚至能夠把()省略掉:

reversed = names.sort { $0 > $1 }

map(_:)函數,它接受一個具備一個參數和一個返回值的閉包,它會遍歷sortArr數組的每個元素並將其賦給閉包參數,閉包的返回值將組成一個新的數組做爲map函數的最終返回。新生產的數組元素與以前的數組一一對應

let sortArr = [1,2,3,4]
let strArr = sortArr.map{ (temp) -> String in   // 結果 strArr = ["4", "5", "6", "7"]
    return "\(temp + 3)"
}

捕獲值(Capturing Values)

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

逃逸閉包

當一個閉包做爲參數傳到一個函數中,可是這個閉包在函數返回以後才被執行,咱們稱該閉包從函數中逃逸。能夠在參數名以前標註@noescape,用來指明這個閉包是不容許「逃逸」出這個函數的,即這個函數執行完成以後閉包就被釋放了。若是強制給一個不容許「逃逸」的閉包賦值給全局變量則會編譯錯誤

將閉包做爲參數傳遞給函數時,你能得到一樣的延時求值行爲。

// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serveCustomer(customerProvider: () -> String) {
    print("Now serving \\(customerProvider())!")
}
serveCustomer( { customersInLine.removeAtIndex(0) } )
// prints "Now serving Alex!"

serveCustomer(_:)接受一個返回顧客名字的顯式的閉包。下面這個版本的serveCustomer(_:)完成了相同的操做,不過它並無接受一個顯式的閉包,而是經過將參數標記爲@autoclosure來接收一個自動閉包。如今你能夠將該函數當作接受String類型參數的函數來調用。customerProvider參數將自動轉化爲一個閉包,由於該參數被標記了@autoclosure特性。

// customersInLine is ["Ewa", "Barry", "Daniella"]
func serveCustomer(@autoclosure customerProvider: () -> String) {
    print("Now serving \\(customerProvider())!")
}
serveCustomer(customersInLine.removeAtIndex(0))
// prints "Now serving Ewa!"

@autoclosure特性暗含了@noescape特性,若是你想讓這個閉包能夠「逃逸」,則應該使用@autoclosure(escaping)特性.

相關文章
相關標籤/搜索