Swift3.0_閉包(Closure)

閉包基礎(chǔ)

** 閉包是自包含的函數(shù)代碼塊陕悬,可以在代碼中被傳遞和使用格嘁。Swift 中的閉包與 C 和 Objective-C 中的代碼塊(blocks)以及其他一些編程語(yǔ)言中的匿名函數(shù)比較相似 **

如果要使用閉包,首先要閉包賦值給變量或者常量
定義一個(gè)可以持有閉包的變量

var multiplyClosure: (Int, Int) -> Int

將閉包賦值給前面定義的變量

multiplyClosure = { (a: Int, b: Int) -> Int in
      return a * b
}

使用定義好的閉包

let result = multiplyClosure(4, 2)
// result is 8

閉包函數(shù)的簡(jiǎn)易寫(xiě)法

  1. 如果閉包只聲明了一個(gè)返回值柒傻,那么可以將return省略掉
multiplyClosure = { (a: Int, b: Int) -> Int in
        a*b
}
  1. 可以省略所有的類型信息,因?yàn)榍懊娑x的變量中已經(jīng)指明了閉包的參數(shù)類型和返回值類型
multiplyClosure = { (a, b) in
        a*b
}
  1. 省略掉所有參數(shù)列表难述。閉包中可以通過(guò)$加一個(gè)從0開(kāi)始的數(shù)字來(lái)表示參數(shù)。
    不過(guò)這種寫(xiě)法個(gè)人認(rèn)為可讀性不強(qiáng)互艾,不建議這么寫(xiě)
multiplyClosure = {
      $0 * $1
}

下面來(lái)通過(guò)一個(gè)例子來(lái)簡(jiǎn)單的使用一下這幾種寫(xiě)法

首先定義一個(gè)函數(shù),其中第三個(gè)參數(shù)是一個(gè)函數(shù)

func operateOnNumbers(_ a: Int, _ b: Int, operation: (Int,Int) -> Int) -> Int{
    let result = operation(a, b)
    print(result)
    return result
}

接著調(diào)用這個(gè)函數(shù)

let addClosure = { (a: Int, b: Int) in   //定義一個(gè)閉包  
        a+b
}
operateOnNumbers(4, 2, operation: addClosure)

當(dāng)然讯泣,operation這個(gè)參數(shù)纫普,也可以直接傳函數(shù),傳閉包是因?yàn)殚]包也是沒(méi)有名稱的函數(shù)好渠,對(duì)于operateOnNumbers來(lái)說(shuō)昨稼,調(diào)用閉包或者函數(shù)對(duì)它來(lái)說(shuō)沒(méi)有區(qū)別

func addFunction(_ a: Int, _ b: Int) -> Int {
return a + b}
operateOnNumbers(4, 2, operation: addFunction)

下面來(lái)看幾種簡(jiǎn)易的寫(xiě)法

operateOnNumbers(3, 4, operation: {$0 * $1}) //上文的第三種簡(jiǎn)寫(xiě)

operateOnNumbers(3, 4, operation: {(a,b) in
      a * b   //第二種,省略類型的簡(jiǎn)寫(xiě)
})
operateOnNumbers(3, 4, operation: {
    (a: Int, b: Int) -> Int in //第一種省略return
    a * b
})
operateOnNumbers(3, 4, operation:  * ) //最簡(jiǎn)易的寫(xiě)法

尾隨閉包

operateOnNumbers(3, 4){
    $0 * $1   // 這種簡(jiǎn)寫(xiě)比較特殊拳锚,只有當(dāng)閉包是作為函數(shù)的最后一個(gè)參數(shù)的時(shí)候才可以這么用
}

定義無(wú)參數(shù)無(wú)返回值的閉包

let voidClosure: () -> Void = {
 print("Swift Apprentice is awesome!")}
voidClosure()

閉包--值捕獲

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

swift中匾荆,可以捕獲值的閉包的最簡(jiǎn)單形式是嵌套函數(shù)拌蜘,也就是定義在其他函數(shù)的函數(shù)體內(nèi)的函數(shù)。嵌套函數(shù)可以捕獲其外部函數(shù)所有的參數(shù)以及定義的常量和變量

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

incrementer閉包可以捕獲makeIncrementer(forIncrement:)中的runningTotal和amount變量的引用牙丽,捕獲引用保證了runningTotal和amount變量在調(diào)用完makeIncrementer后不會(huì)消失简卧,并且保證了在下一次執(zhí)行incrementer函數(shù)時(shí),runningTotal依舊存在烤芦。

let incrementByTen = makeIncrementor(forIncrement: 10)
incrementByTen()// 返回的值為10
incrementByTen()// 返回的值為20
incrementByTen()// 返回的值為30

let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementBySeven()// 返回的值為7

incrementByTen()// 返回的值為40

可以看到incrementByTen中的變量和incrementBySeven中的變量沒(méi)有任何聯(lián)系举娩,而且,他們分別保持著對(duì)變量的引用构罗。

閉包是引用類型

** 函數(shù)和閉包都是引用類型 **

無(wú)論你講函數(shù)或閉包賦值給一個(gè)常量還是變量铜涉,實(shí)際上都是講常量或變量的值設(shè)置為對(duì)應(yīng)的函數(shù)或閉包的引用。 上面的例子中绰播,指向閉包的引用incrementByTen是一個(gè)常量骄噪,而并非閉包內(nèi)容本身。

如果將閉包賦值給了兩個(gè)不同的常量或變量蠢箩,兩個(gè)值都會(huì)指向同一個(gè)閉包:

let alsoIncrementByTen = incrementByTenalso
IncrementByTen()// 返回的值為50

逃逸閉包(Escaping Closures)

當(dāng)一個(gè)閉包作為參數(shù)傳到一個(gè)函數(shù)中,但是這個(gè)閉包在函數(shù)返回之后才被執(zhí)行事甜,稱閉包從函數(shù)中逃逸谬泌。
當(dāng)定義接受閉包作為參數(shù)的函數(shù)時(shí),可以在參數(shù)名之前標(biāo)注@escaping逻谦,用來(lái)指明這個(gè)閉包是允許“逃逸”出這個(gè)函數(shù)的掌实。

一種能使閉包逃逸出函數(shù)的方法是,將這個(gè)閉包保存在函數(shù)外部定義的變量中邦马。
例如:很多啟動(dòng)異步操作的函數(shù)接受一個(gè)閉包參數(shù)作為completion handler贱鼻。這類函數(shù)會(huì)在異步操作開(kāi)始之后立刻返回,但是閉包知道異步操作結(jié)束后才會(huì)被調(diào)用滋将。
這種情況邻悬,閉包需要逃逸出函數(shù),因?yàn)殚]包需要在函數(shù)返回之后被調(diào)用随闽。

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

如果不加@escaping關(guān)鍵字父丰,將會(huì)得到一個(gè)編譯錯(cuò)誤

將一個(gè)閉包標(biāo)記為@escaping意味著你必須在閉包中顯示地引用self

var completionHandlers: [() -> Void] = []

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

func someFunctionWithNoneEscapingClosure(closure: () -> Void){
    closure()
}

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

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

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

自動(dòng)閉包

自動(dòng)閉包是一種自動(dòng)創(chuàng)建的閉包,用于包裝傳遞給函數(shù)作為參數(shù)的表達(dá)式掘宪。這種閉包不接受任何參數(shù)蛾扇,當(dāng)它被調(diào)用的時(shí)候,會(huì)返回被包裝在其中的表達(dá)式的值魏滚。

自動(dòng)閉包能夠延遲求職镀首,直到調(diào)用這個(gè)閉包,代碼段才回被執(zhí)行鼠次。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]print(customersInLine.count)
// 打印出 "5"
let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出 "5"
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// 打印出 "4"

只有當(dāng)閉包c(diǎn)ustomerProvider執(zhí)行時(shí)更哄,才會(huì)執(zhí)行remove操作

利用閉包來(lái)自定義排序方法

數(shù)組的排序

let names = ["ZZZZZZ", "BB", "A", "CCCC", "EEEEE"]names.sorted()
// ["A", "BB", "CCCC", "EEEEE", "ZZZZZZ"]

可以通過(guò)制定閉包來(lái)為數(shù)組指定一個(gè)新的排序方式

names.sorted {
 $0.characters.count > $1.characters.count}
// ["ZZZZZZ", "EEEEE", "CCCC", "BB", "A"]

用閉包來(lái)遍歷集合

集合實(shí)現(xiàn)了很多很實(shí)用的功能芋齿,比如排序,篩選等等竖瘾。
這些功能都配合閉包實(shí)用沟突,來(lái)定義不同的規(guī)則

var prices = [1.5, 10, 4.99, 2.30, 8.19]
let largePrices = prices.filter { 
          return $0 > 5
}

利用閉包配合map方法,可以將數(shù)組里面的每個(gè)值捕传,進(jìn)行指定的操作惠拭,并返回一個(gè)新的數(shù)組

let salePrices = prices.map {
 return $0 * 0.9}
let sum = prices.reduce(0) {
     return $0 + $1
}

同樣的,也可以對(duì)字典進(jìn)行對(duì)應(yīng)的操作

let stock = [1.5:5, 10:2, 4.99:20, 2.30:5, 8.19:30]let stockSum = stock.reduce(0) {
 return $0 + $1.key * Double($1.value)}

字典遍歷的時(shí)候返回的是一個(gè)元組庸论,所以可以分別拿到key和value進(jìn)行操作

舉個(gè)例子來(lái)說(shuō)明下上面幾個(gè)集合的功能函數(shù)配合閉包的用法

let namesAndAges = ["Owen": 40, "Pogba": 21, "Martial": 21, "Rashford": 19, "Marry": 17, "Lina": 15]

// 使用reduce將數(shù)組中的名字連成一串

names.reduce(""){
    return $0 + $1
}

//將上面的數(shù)組职辅,先篩選出所有名字超過(guò)4個(gè)字符的,然后將數(shù)組中的名字連成一串

let filteredNames = names.filter{
    return $0.characters.count > 4
}
filteredNames.reduce(""){
    $0 + $1
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末聂示,一起剝皮案震驚了整個(gè)濱河市域携,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鱼喉,老刑警劉巖秀鞭,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異扛禽,居然都是意外死亡锋边,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門编曼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)豆巨,“玉大人,你說(shuō)我怎么就攤上這事掐场⊥樱” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵熊户,是天一觀的道長(zhǎng)萍膛。 經(jīng)常有香客問(wèn)我,道長(zhǎng)敏弃,這世上最難降的妖魔是什么卦羡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮麦到,結(jié)果婚禮上绿饵,老公的妹妹穿的比我還像新娘。我一直安慰自己瓶颠,他們只是感情好拟赊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著粹淋,像睡著了一般吸祟。 火紅的嫁衣襯著肌膚如雪瑟慈。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天屋匕,我揣著相機(jī)與錄音葛碧,去河邊找鬼。 笑死过吻,一個(gè)胖子當(dāng)著我的面吹牛进泼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纤虽,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼乳绕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了逼纸?” 一聲冷哼從身側(cè)響起洋措,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杰刽,沒(méi)想到半個(gè)月后菠发,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贺嫂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年雷酪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涝婉。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蔗怠,靈堂內(nèi)的尸體忽然破棺而出墩弯,到底是詐尸還是另有隱情,我是刑警寧澤寞射,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布渔工,位于F島的核電站,受9級(jí)特大地震影響桥温,放射性物質(zhì)發(fā)生泄漏引矩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一侵浸、第九天 我趴在偏房一處隱蔽的房頂上張望旺韭。 院中可真熱鬧,春花似錦掏觉、人聲如沸区端。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)织盼。三九已至杨何,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沥邻,已是汗流浹背危虱。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唐全,地道東北人埃跷。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像芦瘾,于是被迫代替她去往敵國(guó)和親捌蚊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

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