swift學(xué)習(xí)筆記2——函數(shù)、閉包

之前學(xué)習(xí)swift時(shí)的個(gè)人筆記言秸,根據(jù)github:the-swift-programming-language-in-chinese學(xué)習(xí)、總結(jié)迎捺,將重要的內(nèi)容提取举畸,加以理解后整理為學(xué)習(xí)筆記,方便以后查詢用凳枝。詳細(xì)可以參考the-swift-programming-language-in-chinese抄沮,或者蘋果官方英文版文檔

當(dāng)前版本是swift2.2

函數(shù)

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

函數(shù)參數(shù)名稱(Function Parameter Names)

函數(shù)參數(shù)都有一個(gè)外部參數(shù)名(external parameter name)和一個(gè)局部參數(shù)名(local parameter name)跋核。外部參數(shù)名用于在函數(shù)調(diào)用時(shí)標(biāo)注傳遞給函數(shù)的參數(shù),局部參數(shù)名在函數(shù)的實(shí)現(xiàn)內(nèi)部使用叛买。

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)

一般情況下砂代,第一個(gè)參數(shù)省略其外部參數(shù)名,第二個(gè)以及隨后的參數(shù)使用其局部參數(shù)名作為外部參數(shù)名率挣。所有參數(shù)必須有獨(dú)一無二的局部參數(shù)名刻伊。盡管多個(gè)參數(shù)可以有相同的外部參數(shù)名,但不同的外部參數(shù)名能讓你的代碼更有可讀性椒功。

你可以在局部參數(shù)名前指定外部參數(shù)名捶箱,中間以空格分隔:

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

如果你提供了外部參數(shù)名(包括局部參數(shù)名作為外部參數(shù)名的情況),那么函數(shù)在被調(diào)用時(shí)动漾,必須使用外部參數(shù)名丁屎。如果想忽略外部參數(shù)名,使用_替代即可

默認(rèn)參數(shù)值(Default Parameter Values)

你可以在函數(shù)體中為每個(gè)參數(shù)定義默認(rèn)值(Deafult Values)旱眯。當(dāng)默認(rèn)值被定義后晨川,調(diào)用這個(gè)函數(shù)時(shí)可以忽略這個(gè)參數(shù)。

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

將帶有默認(rèn)值的參數(shù)放在函數(shù)參數(shù)列表的最后键思。這樣可以保證在函數(shù)調(diào)用時(shí)础爬,非默認(rèn)參數(shù)的順序是一致的,同時(shí)使得相同的函數(shù)在不同情況下調(diào)用時(shí)顯得更為清晰吼鳞。不過不放在最后也是可以的

可變參數(shù)(Variadic Parameters)

一個(gè)可變參數(shù)(variadic parameter)可以接受零個(gè)或多個(gè)值看蚜。函數(shù)調(diào)用時(shí),你可以用可變參數(shù)來指定函數(shù)參數(shù)可以被傳入不確定數(shù)量的輸入值赔桌。通過在變量類型名后面加入(...)的方式來定義可變參數(shù)供炎。

可變參數(shù)的傳入值在函數(shù)體中變?yōu)榇祟愋偷囊粋€(gè)數(shù)組。例如疾党,一個(gè)叫做 numbers 的 Double... 型可變參數(shù)音诫,在函數(shù)體內(nèi)可以當(dāng)做一個(gè)叫 numbers 的 [Double] 型的數(shù)組常量。

// 求算數(shù)平均數(shù)
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函數(shù)了
func alvalible(num:AnyObject...) {
    print(num[0],num[1])
    
}

常量參數(shù)和變量參數(shù)

函數(shù)參數(shù)默認(rèn)是常量雪位。試圖在函數(shù)體中更改參數(shù)值將會(huì)導(dǎo)致編譯錯(cuò)誤竭钝。
通過在參數(shù)名前加關(guān)鍵字 var 來定義變量參數(shù):

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

swift是值傳遞的,可變參數(shù)作用域只在函數(shù)內(nèi)部雹洗,對(duì)調(diào)用者不產(chǎn)生影響香罐,和C中的指針傳遞不一樣,如果想達(dá)到C指針的效果时肿,可以使用輸入輸出參數(shù)

輸入輸出參數(shù)

變量參數(shù)庇茫,正如上面所述,僅僅能在函數(shù)體內(nèi)被更改螃成。如果你想要一個(gè)函數(shù)可以修改參數(shù)的值旦签,并且想要在這些修改在函數(shù)調(diào)用結(jié)束后仍然存在查坪,那么就應(yīng)該把這個(gè)參數(shù)定義為輸入輸出參數(shù)(In-Out Parameters)。調(diào)用的時(shí)候?qū)崊⒈仨毧勺兡牛壹由?amp;

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

輸入輸出參數(shù)不能有默認(rèn)值偿曙,而且可變參數(shù)不能用 inout 標(biāo)記。如果你用 inout 標(biāo)記一個(gè)參數(shù)淋淀,這個(gè)參數(shù)不能被 var 或者 let 標(biāo)記遥昧。

函數(shù)參數(shù)

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

第一個(gè)函數(shù)的類型是(Int, Int) -> Int,可以解讀為“這個(gè)函數(shù)類型有兩個(gè) Int 型的參數(shù)并返回一個(gè) Int 型的值朵纷√砍簦”。第二個(gè)() -> Void

在 Swift 中袍辞,使用函數(shù)類型就像使用其他類型一樣鞋仍。例如,你可以定義一個(gè)類型為函數(shù)的常量或變量搅吁,并將適當(dāng)?shù)暮瘮?shù)賦值給它:

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

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

這個(gè)可以解讀為:

“定義一個(gè)叫做 mathFunction 的變量威创,類型是‘一個(gè)有兩個(gè) Int 型的參數(shù)并返回一個(gè) Int 型的值的函數(shù)’,并讓這個(gè)新變量指向 addTwoInts 函數(shù)”谎懦。

就像其他類型一樣肚豺,當(dāng)賦值一個(gè)函數(shù)給常量或變量時(shí),你可以讓 Swift 來推斷其函數(shù)類型:

let anotherMathFunction = addTwoInts

參數(shù)為函數(shù)類型

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

函數(shù)類型作為返回類型

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
}

嵌套函數(shù)

嵌套函數(shù)(Nested Functions)
這章中你所見到的所有函數(shù)都叫全局函數(shù)(global functions)界拦,它們定義在全局域中吸申。你也可以把函數(shù)定義在別的函數(shù)體中,稱作嵌套函數(shù)(nested functions)享甸。

默認(rèn)情況下截碴,嵌套函數(shù)是對(duì)外界不可見的,但是可以被它們的外圍函數(shù)(被嵌套的函數(shù)即例中的chooseStepFunction)調(diào)用蛉威。一個(gè)外圍函數(shù)也可以返回它的某一個(gè)嵌套函數(shù)日丹,使得這個(gè)函數(shù)可以在其他域中被使用。

你可以用返回嵌套函數(shù)的方式重寫 chooseStepFunction(_:) 函數(shù):

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!

閉包

閉包表達(dá)式語法有如下一般形式:

{ (parameters) -> returnType in
    statements
}

閉包表達(dá)式語法可以使用常量蚯嫌、變量和inout類型作為參數(shù)哲虾,不能提供默認(rèn)值。也可以在參數(shù)列表的最后使用可變參數(shù)择示。元組也可以作為參數(shù)和返回值束凑。

sort(_:)方法接受一個(gè)閉包,該閉包函數(shù)需要傳入與數(shù)組元素類型相同的兩個(gè)值对妄,并返回一個(gè)布爾類型值來表明當(dāng)排序結(jié)束后傳入的第一個(gè)參數(shù)排在第二個(gè)參數(shù)前面還是后面。如果第一個(gè)參數(shù)值出現(xiàn)在第二個(gè)參數(shù)值前面敢朱,排序閉包函數(shù)需要返回true剪菱,反之返回false摩瞎。sort函數(shù)會(huì)將數(shù)組里面的元素按照冒泡排序一樣的返回給s1,s2兩個(gè)參數(shù)孝常,然后進(jìn)行比較排序旗们。

函數(shù)對(duì)應(yīng)的閉包表達(dá)式版本的代碼:

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

然而在內(nèi)聯(lián)閉包表達(dá)式中,函數(shù)和返回值類型都寫在大括號(hào)內(nèi)构灸,而不是大括號(hào)外上渴。
閉包的函數(shù)體部分由關(guān)鍵字in引入。該關(guān)鍵字表示閉包的參數(shù)和返回值類型定義已經(jīng)完成喜颁,閉包函數(shù)體即將開始稠氮。

因?yàn)榕判蜷]包函數(shù)是作為sort(_:)方法的參數(shù)傳入的,Swift 可以推斷其參數(shù)和返回值的類型半开。sort(_:)方法被一個(gè)字符串?dāng)?shù)組調(diào)用隔披,因此其參數(shù)必須是(String, String) -> Bool類型的函數(shù)。這意味著(String, String)和Bool類型并不需要作為閉包表達(dá)式定義的一部分删掀。因?yàn)樗械念愋投伎梢员徽_推斷噪珊,返回箭頭(->)和圍繞在參數(shù)周圍的括號(hào)也可以被省略:

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

實(shí)際上任何情況下检激,通過內(nèi)聯(lián)閉包表達(dá)式構(gòu)造的閉包作為參數(shù)傳遞給函數(shù)或方法時(shí),都可以推斷出閉包的參數(shù)和返回值類型鬓长。 這意味著閉包作為函數(shù)或者方法的參數(shù)時(shí),您幾乎不需要利用完整格式構(gòu)造內(nèi)聯(lián)閉包尝江。

單行表達(dá)式閉包可以通過省略return關(guān)鍵字來隱式返回單行表達(dá)式的結(jié)果涉波,如上版本的例子可以改寫為:

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

在這個(gè)例子中,sort(_:)方法的第二個(gè)參數(shù)函數(shù)類型明確了閉包必須返回一個(gè)Bool類型值茂装。因?yàn)殚]包函數(shù)體只包含了一個(gè)單一表達(dá)式(s1 > s2)怠蹂,該表達(dá)式返回Bool類型值,因此這里沒有歧義少态,return關(guān)鍵字可以省略城侧。

參數(shù)名稱縮寫

Swift 自動(dòng)為內(nèi)聯(lián)閉包提供了參數(shù)名稱縮寫功能,您可以直接通過$0彼妻,$1嫌佑,$2來順序調(diào)用閉包的參數(shù),以此類推侨歉。

尾隨閉包(Trailing Closures)

如果您需要將一個(gè)很長(zhǎng)的閉包表達(dá)式作為最后一個(gè)參數(shù)傳遞給函數(shù)屋摇,可以使用尾隨閉包來增強(qiáng)函數(shù)的可讀性。尾隨閉包是一個(gè)書寫在函數(shù)括號(hào)之后的閉包表達(dá)式幽邓,函數(shù)支持將其作為最后一個(gè)參數(shù)調(diào)用:

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

// 以下是不使用尾隨閉包進(jìn)行函數(shù)調(diào)用
someFunctionThatTakesAClosure({
    // 閉包主體部分
})

// 以下是使用尾隨閉包進(jìn)行函數(shù)調(diào)用
someFunctionThatTakesAClosure() {
    // 閉包主體部分
}

在閉包表達(dá)式語法一節(jié)中作為sort(_:)方法參數(shù)的字符串排序閉包可以改寫為:

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

如果函數(shù)只需要閉包表達(dá)式一個(gè)參數(shù)炮温,當(dāng)您使用尾隨閉包時(shí),您甚至可以把()省略掉:

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

map(_:)函數(shù)牵舵,它接受一個(gè)具有一個(gè)參數(shù)和一個(gè)返回值的閉包柒啤,它會(huì)遍歷sortArr數(shù)組的每一個(gè)元素并將其賦給閉包參數(shù)倦挂,閉包的返回值將組成一個(gè)新的數(shù)組作為map函數(shù)的最終返回。新生產(chǎn)的數(shù)組元素與之前的數(shù)組一一對(duì)應(yīng)

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

捕獲值(Capturing Values)

閉包可以在其被定義的上下文中捕獲常量或變量担巩。即使定義這些常量和變量的原作用域已經(jīng)不存在方援,閉包仍然可以在閉包函數(shù)體內(nèi)引用和修改這些值。

逃逸閉包

當(dāng)一個(gè)閉包作為參數(shù)傳到一個(gè)函數(shù)中涛癌,但是這個(gè)閉包在函數(shù)返回之后才被執(zhí)行犯戏,我們稱該閉包從函數(shù)中逃逸∪埃可以在參數(shù)名之前標(biāo)注@noescape先匪,用來指明這個(gè)閉包是不允許“逃逸”出這個(gè)函數(shù)的,即這個(gè)函數(shù)執(zhí)行完成之后閉包就被釋放了假颇。如果強(qiáng)制給一個(gè)不允許“逃逸”的閉包賦值給全局變量則會(huì)編譯錯(cuò)誤

將閉包作為參數(shù)傳遞給函數(shù)時(shí)胚鸯,你能獲得同樣的延時(shí)求值行為。

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

serveCustomer(_:)接受一個(gè)返回顧客名字的顯式的閉包笨鸡。下面這個(gè)版本的serveCustomer(_:)完成了相同的操作姜钳,不過它并沒有接受一個(gè)顯式的閉包,而是通過將參數(shù)標(biāo)記為@autoclosure來接收一個(gè)自動(dòng)閉包⌒魏模現(xiàn)在你可以將該函數(shù)當(dāng)做接受String類型參數(shù)的函數(shù)來調(diào)用哥桥。customerProvider參數(shù)將自動(dòng)轉(zhuǎn)化為一個(gè)閉包,因?yàn)樵搮?shù)被標(biāo)記了@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特性,如果你想讓這個(gè)閉包可以“逃逸”拟糕,則應(yīng)該使用@autoclosure(escaping)特性.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市倦踢,隨后出現(xiàn)的幾起案子送滞,更是在濱河造成了極大的恐慌,老刑警劉巖辱挥,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件犁嗅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡晤碘,警方通過查閱死者的電腦和手機(jī)褂微,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來园爷,“玉大人宠蚂,你說我怎么就攤上這事⊥纾” “怎么了求厕?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我呀癣,道長(zhǎng)旅东,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任十艾,我火速辦了婚禮,結(jié)果婚禮上腾节,老公的妹妹穿的比我還像新娘忘嫉。我一直安慰自己,他們只是感情好案腺,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布庆冕。 她就那樣靜靜地躺著,像睡著了一般劈榨。 火紅的嫁衣襯著肌膚如雪访递。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天同辣,我揣著相機(jī)與錄音拷姿,去河邊找鬼。 笑死旱函,一個(gè)胖子當(dāng)著我的面吹牛响巢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棒妨,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼踪古,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了券腔?” 一聲冷哼從身側(cè)響起伏穆,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纷纫,沒想到半個(gè)月后枕扫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涛酗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年铡原,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片商叹。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡燕刻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出剖笙,到底是詐尸還是另有隱情卵洗,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站过蹂,受9級(jí)特大地震影響十绑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜酷勺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一本橙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧脆诉,春花似錦甚亭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至偶摔,卻和暖如春暇唾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辰斋。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工策州, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宫仗。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓抽活,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親锰什。 傳聞我的和親對(duì)象是個(gè)殘疾皇子下硕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容