swift的閉包

代碼練習(xí)閉包舟茶。

//
// main.swift
// SwiftLearn9-閉包
//
// Created by iOS on 2018/4/17.
// Copyright ? 2018年 weiman. All rights reserved.
//

import Foundation

print("Hello, World!")

/** 一川陆、
閉包是自包含的代碼塊,可以在代碼中被傳遞和使用售貌。
swift的閉包與C和OC的Block以及其他一些語言的匿名函數(shù)比較相似。
*/

/*
閉包采取如下三種形式之一:
1.全局函數(shù)是一個有名字但不會捕獲任何值的閉包。
2.嵌套函數(shù)是一個有名字并可以捕獲其封閉函數(shù)域內(nèi)值的閉包
3.閉包表達(dá)式是一個利用輕量級語法所寫的可以捕獲上下文中變量或者常量值的匿名閉包

*/

/*
swift閉包表達(dá)式擁有簡潔的風(fēng)格碗啄,并鼓勵優(yōu)化笔宿。主要優(yōu)化如下:
1.利用上下文推斷參數(shù)和返回值類型
2.隱式返回單表達(dá)式閉包犁钟,即單表達(dá)式閉包可以省略關(guān)鍵字return
3.參數(shù)名稱縮寫
4.尾隨閉包語法
*/

do {
    // 數(shù)組排序
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    
    func backward(s1: String, s2: String) -> Bool {
        return s1 > s2
    }
    // 名字按照字母倒序排列
    var reversedNames = names.sorted(by: backward)
    print("使用函數(shù)棱诱。\n \(reversedNames)")
    
    // 使用閉包
    reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
        return s1 > s2
    })
    print("使用閉包。\n \(reversedNames)")
    
    /*
     閉包的函數(shù)體部分由關(guān)鍵字in引入涝动。該關(guān)鍵字表示閉包的參數(shù)和返回值類型定義已經(jīng)完成军俊,閉包函數(shù)體即將開始。
     */
    
    //閉包優(yōu)化1: 根據(jù)上下文推斷類型
    
    // 根據(jù)數(shù)組內(nèi)的元素類型可以推斷出參數(shù)的類型捧存,所以粪躬,參數(shù)類型可以省略
    reversedNames = names.sorted(by: { (s1, s2) -> Bool in return s1 > s2 })
    print("根據(jù)上下文推斷類型。\n \(reversedNames)")
    
    //閉包優(yōu)化2: 單表達(dá)式閉包隱式返回
    
    //單行表達(dá)式可以省略return關(guān)鍵字
    reversedNames = names.sorted(by: { (s1, s2) -> Bool in s1 > s2 })
    print("單表達(dá)式閉包隱式返回\n  \(reversedNames)")
    
    //閉包優(yōu)化3: 參數(shù)名稱縮寫
    
    /*Swift 自動為內(nèi)聯(lián)閉包提供了參數(shù)名稱縮寫功能昔穴,你可以直接通過 $0镰官,$1,$2 來順序調(diào)用閉包的參數(shù)吗货,以此類推.
     如果你在閉包表達(dá)式中使用參數(shù)名稱縮寫泳唠,你可以在閉包定義中省略參數(shù)列表,并且對應(yīng)參數(shù)名稱縮寫的類型會通過函數(shù)類型進(jìn)行推斷宙搬。in關(guān)鍵字也同樣可以被省略笨腥,因?yàn)榇藭r閉包表達(dá)式完全由閉包函數(shù)體構(gòu)成:
 */
    reversedNames = names.sorted(by: { $0 > $1 })
    print("參數(shù)名稱縮寫\n  \(reversedNames)")
    
    //閉包優(yōu)化4: 運(yùn)算符方法
    /*
     Swift 的 String 類型定義了關(guān)于大于號(>)的字符串實(shí)現(xiàn),其作為一個函數(shù)接受兩個 String 類型的參數(shù)并返回 Bool 類型的值勇垛。而這正好與 sorted(by:) 方法的參數(shù)需要的函數(shù)類型相符合脖母。因此,你可以簡單地傳遞一個大于號闲孤,Swift 可以自動推斷出你想使用大于號的字符串函數(shù)實(shí)現(xiàn):
     */
    reversedNames = names.sorted(by: >)
    print("運(yùn)算符方法\n  \(reversedNames)")
    
    
}

// 二谆级、尾隨閉包
// 如果你需要將一個很長的閉包表達(dá)式作為最后一個參數(shù)傳遞給函數(shù),可以使用尾隨閉包來增強(qiáng)函數(shù)的可讀性讼积。
// 尾隨閉包肥照,顧名思義就是閉包作為函數(shù)的最后一個參數(shù)。

do {
    let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    var reversedNames = names.sorted(){ $0 > $1 }
    print("尾隨閉包\n  \(reversedNames)")
    
    //如果閉包表達(dá)式是函數(shù)或方法的唯一參數(shù)勤众,則當(dāng)你使用尾隨閉包時舆绎,你甚至可以把 () 省略
    
    reversedNames = names.sorted { $0 > $1 }
    print("尾隨閉包簡化\n  \(reversedNames)")
    
    
}

/*
 使用尾隨閉包將 Int 類型數(shù)組 [16, 58, 510] 轉(zhuǎn)換為包含對應(yīng) String 類型的值的數(shù)組["OneSix", "FiveEight", "FiveOneZero"]
 */

do {
    let digitNames = [
        0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
        5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
    ]
    
    let numbers = [16, 58, 510]
    
    let strings  = numbers.map { (number) -> String in
        var number = number
        var output = ""
        repeat {
            output = digitNames[number % 10]! + output
            number /= 10
        } while number > 0
        
        return output
    }
    
    print("---- \(strings)")
}

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

Swift 中掌桩,可以捕獲值的閉包的最簡單形式是嵌套函數(shù)边锁,也就是定義在其他函數(shù)的函數(shù)體內(nèi)的函數(shù)。嵌套函數(shù)可以捕獲其外部函數(shù)所有的參數(shù)以及定義的常量和變量波岛。

舉個例子茅坛,這有一個叫做 makeIncrementer 的函數(shù),其包含了一個叫做 incrementer 的嵌套函數(shù)。嵌套函數(shù) incrementer() 從上下文中捕獲了兩個值贡蓖,runningTotal 和 amount曹鸠。捕獲這些值之后,makeIncrementer 將 incrementer 作為閉包返回斥铺。每次調(diào)用 incrementer 時彻桃,其會以 amount 作為增量增加 runningTotal 的值。
*/

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

let incrementByTen = makeIncrementer(forIncrement: 10)
print("-----1-----  \(incrementByTen())")
print("-----2-----  \(incrementByTen())")
print("-----3-----  \(incrementByTen())")
print("-----4-----  \(incrementByTen())")
/*
 結(jié)果是:
 -----1-----  10
 -----2-----  20
 -----3-----  30
 -----4-----  40
 */

/*
解釋:
捕獲引用保證了 runningTotal 和 amount 變量在調(diào)用完 makeIncrementer 后不會消失晾蜘,并且保證了在下一次執(zhí)行 incrementer 函數(shù)時邻眷,runningTotal 依舊存在。

注意 為了優(yōu)化剔交,如果一個值不會被閉包改變肆饶,或者在閉包創(chuàng)建后不會改變,Swift 可能會改為捕獲并保存一份對值的拷貝岖常。 Swift 也會負(fù)責(zé)被捕獲變量的所有內(nèi)存管理工作驯镊,包括釋放不再需要的變量。
*/

/*
如果你創(chuàng)建了另一個 incrementer竭鞍,它會有屬于自己的引用板惑,指向一個全新、獨(dú)立的 runningTotal 變量:
*/

let incrementBySeven = makeIncrementer(forIncrement: 7)
print("--aa--- \(incrementBySeven())")

/*
注意:
如果你將閉包賦值給一個類實(shí)例的屬性偎快,并且該閉包通過訪問該實(shí)例或其成員而捕獲了該實(shí)例冯乘,你將在閉包和該實(shí)例間創(chuàng)建一個循環(huán)強(qiáng)引用。Swift 使用捕獲列表來打破這種循環(huán)強(qiáng)引用
*/

/*
四滨砍、閉包是引用類型
上面的例子中往湿,incrementBySeven 和 incrementByTen 都是常量妖异,但是這些常量指向的閉包仍然可以增加其捕獲的變量的值惋戏。這是因?yàn)楹瘮?shù)和閉包都是引用類型。
這也意味著如果你將閉包賦值給了兩個不同的常量或變量他膳,兩個值都會指向同一個閉包响逢。
*/

let alsoIncrementByTen = incrementByTen
print("--bb-- \(alsoIncrementByTen())")

/*
打印結(jié)果:
--bb-- 50
*/

/*
五、逃逸閉包
當(dāng)一個閉包作為參數(shù)傳到一個函數(shù)中棕孙,但是這個閉包在函數(shù)返回之后才被執(zhí)行舔亭,我們稱該閉包從函數(shù)中逃逸。
當(dāng)你定義接受閉包作為參數(shù)的函數(shù)時蟀俊,你可以在參數(shù)名之前標(biāo)注 @escaping钦铺,用來指明這個閉包是允許“逃逸”出這個函數(shù)的。
*/

// 一個數(shù)組肢预,數(shù)組的元素是無參數(shù)矛洞,無返回值的函數(shù)
var completionHandlers: [() -> Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

//將一個閉包標(biāo)記為 @escaping 意味著你必須在閉包中顯式地引用 self。
func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// 打印出 "200"

completionHandlers.first?()
print(instance.x)
// 打印出 "100"

/*
六烫映、自動閉包
自動閉包是一種自動創(chuàng)建的閉包沼本,用于包裝傳遞給函數(shù)作為參數(shù)的表達(dá)式噩峦。
這種閉包不接受任何參數(shù),當(dāng)它被調(diào)用的時候抽兆,會返回被包裝在其中的表達(dá)式的值识补。這種便利語法讓你能夠省略閉包的花括號,用一個普通的表達(dá)式來代替顯式的閉包辫红。
*/

do {
    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    print(customersInLine.count)
    let customerProvider = { customersInLine.remove(at : 0) }
    print("customersInLine: \(customersInLine.count)")
    print("customerProvider = \(customerProvider())")
    print("customersInLine: \(customersInLine.count)")
    /*
     打印結(jié)果:
     5
     customersInLine: 5
     customerProvider = Chris
     customersInLine: 4
     由此可以看出凭涂,在調(diào)用customerProvider之前,花括號中的語句體都沒有執(zhí)行贴妻,直到customerProvider調(diào)用才執(zhí)行导盅。
     */
    //將閉包作為參數(shù)傳遞給函數(shù)時,你能獲得同樣的延時求值行為揍瑟。


    func serve(customer customerProvider: () -> String) {
        print("Now serving \(customerProvider())!")
    }
    serve(customer: { customersInLine.remove(at: 0) } )
    // 輸出:Now serving Alex!

/*
 注意
 過度使用 autoclosures 會讓你的代碼變得難以理解白翻。上下文和函數(shù)名應(yīng)該能夠清晰地表明求值是被延遲執(zhí)行的。
 */

}

// 個人認(rèn)為绢片,代碼的書寫應(yīng)該盡量的邏輯清晰滤馍,易讀,過多的使用閉包或許會讓代碼過于晦澀難懂底循。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巢株,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子熙涤,更是在濱河造成了極大的恐慌阁苞,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祠挫,死亡現(xiàn)場離奇詭異那槽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)等舔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門骚灸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慌植,你說我怎么就攤上這事甚牲。” “怎么了蝶柿?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵丈钙,是天一觀的道長。 經(jīng)常有香客問我交汤,道長雏赦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮喉誊,結(jié)果婚禮上邀摆,老公的妹妹穿的比我還像新娘。我一直安慰自己伍茄,他們只是感情好栋盹,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著敷矫,像睡著了一般例获。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上曹仗,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天榨汤,我揣著相機(jī)與錄音,去河邊找鬼怎茫。 笑死收壕,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的轨蛤。 我是一名探鬼主播蜜宪,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼祥山!你這毒婦竟也來了圃验?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤缝呕,失蹤者是張志新(化名)和其女友劉穎澳窑,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體供常,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡摊聋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了话侧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栗精。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瞻鹏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹿寨,我是刑警寧澤新博,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站脚草,受9級特大地震影響赫悄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一埂淮、第九天 我趴在偏房一處隱蔽的房頂上張望姑隅。 院中可真熱鬧,春花似錦倔撞、人聲如沸讲仰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鄙陡。三九已至,卻和暖如春躏啰,著一層夾襖步出監(jiān)牢的瞬間趁矾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工给僵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留毫捣,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓帝际,卻偏偏與公主長得像培漏,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子胡本,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

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