Swift基礎語法(六)閉包表達式和閉包

Swift基礎語法文章匯總

本文主要介紹閉包表達式和閉包蟀拷,閉包表達式簡化了函數(shù)的調用问麸,閉包可以捕獲局部變量丹锹,在局部變量的作用域外也可以進行操作

主要內容:

  1. 閉包表達式
  2. 閉包使用
  3. 閉包原理

1泳姐、閉包表達式

1.1 閉包表達式認識

閉包表達式用來實現(xiàn)功能档泽,類似于函數(shù)的作用坦康,只是寫法不一樣

定義格式:

{
    (參數(shù)列表) -> 返回值類型 in
    函數(shù)體代碼
}

代碼:

/*
 1攻泼、閉包表達式的寫法
 */
//1.1 函數(shù)
func sum(_ v1: Int, _ v2: Int) -> Int {
    v1 + v2
}
print("sum\(sum(10, 20))")

//1.2 閉包表達式
var fn = {
    (v1: Int,v2: Int) -> Int in
    return v1 + v2
}

let result = fn(10,20)

//1.3 匿名閉包表達式
{
   (v1: Int,v2: Int) -> Int in
   return v1 + v2
}(10,20)

說明:

  1. 函數(shù)的功能使用閉包表達式也完全可以實現(xiàn)火架。
  2. 函數(shù)有的元素,閉包表達式也都有忙菠,只是書寫方式不一樣
  3. 注意in是用來分割函數(shù)類型和函數(shù)體的
  4. 閉包表達式需要使用{}來包含進去
  5. 如果需要賦給一個變量何鸡,就通過變量來調用,如果直接使用牛欢,就直接調用傳值

簡寫:

/*
 2骡男、閉包表達式的簡寫
 */
func test2 (){
    //2.1 函數(shù)
    func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
        print(fn(v1, v2))
    }
    //2.2 閉包表達式正常格式
    exec(v1: 10, v2: 20, fn: {
    (v1: Int, v2: Int) -> Int in
    return v1 + v2
    })

    //2.3 簡寫:參數(shù)類型
    exec(v1: 10, v2: 20, fn: {
    v1, v2 in return v1 + v2
    })

    //2.4 簡寫:return
    exec(v1: 10, v2: 20, fn: {
    v1, v2 in v1 + v2
    })

    //2.5 簡寫:參數(shù)
    exec(v1: 10, v2: 20, fn: { $0 + $1 })

    //2.6 簡寫:只寫操作符
    exec(v1: 10, v2: 20, fn: + )
}
test2()

說明:

  1. 可以將參數(shù)類型省略掉,因為在聲明中已經明確了類型
  2. 可以將return省略掉傍睹,因為函數(shù)體只有一個表達式
  3. 可以將參數(shù)省略掉隔盛,此時使用0/1來表示第一個、第二個參數(shù)(因為在聲明中已經明確了參數(shù)類型拾稳,所以此時不用創(chuàng)建參數(shù)吮炕,直接通過占位符使用)
  4. 甚至可以將0/1省略掉,只寫一個運算符(因為可以通過聲明判斷出兩個數(shù)需要進行運算)

注意:

  1. 與函數(shù)不同的是閉包中不用寫參數(shù)標簽也可以在調用時不寫參數(shù)名稱
  2. 在簡寫中访得,也可以不寫參數(shù)名稱龙亲,而是通過0,1來表示第一個陕凹、第二個參數(shù)。此時所有的內容都是函數(shù)體了鳄炉。
  3. 在簡寫中杜耙,還有一種極簡方式,就是只寫操作符拂盯,不寫參數(shù)佑女,這樣編譯器會認為是操作這兩個參數(shù)的。這種就是極簡形式了
  4. 如果不用參數(shù)谈竿,直接返回值珊豹,不能直接省略,而是應該用_來占位一下

1.2 尾隨閉包

如果將一個很長的閉包表達式作為函數(shù)的最后一個實參榕订,可讀性較差,此時可以使用尾隨閉包增加代碼的可讀性蜕便。
尾隨閉包就是將閉包表達式書寫在調用函數(shù)的括號外面

代碼:

func test3 () {
    /*
     3劫恒、尾隨閉包
     */
    //3.1 函數(shù)定義
    func exec1(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
        print(fn(v1, v2))
    })
    
    //3.2 函數(shù)調用時尾隨閉包
    exec(v1: 10, v2: 20) { $0 + $1 }
}

test3()

說明:

  • 可以看fn閉包表達式作為exec函數(shù)的參數(shù),但是本身是放在了括號的后邊轿腺,這就是所謂的尾隨
  • 需要注意的是两嘴,如果閉包表達式是函數(shù)的唯一實參,而且使用了尾隨閉包的語法族壳,就不需要在函數(shù)后面寫()

1.3 案例說明

以數(shù)組排序的實際案例來使用閉包表達式

代碼:

/*
 4憔辫、數(shù)組排序實例
 */
func test4 () {
    //func sort(by areInIncreasingOrder: (Element, Element) -> Bool)
    
    var nums = [11, 2, 18, 6, 5, 68, 45]
    //4.1 sort排序函數(shù)中最后一個參數(shù)設置閉包表達式
    nums.sort(by: {
        (i1: Int, i2: Int) -> Bool in
        return i1 < i2
    })
    
    //各種簡寫
    nums.sort(by: { i1, i2 in return i1 < i2 })//正常
    nums.sort(by: { i1, i2 in i1 < i2 })//沒有return
    nums.sort(by: { $0 < $1 })//沒有參數(shù)
    nums.sort(by: <)//只有運算符
    nums.sort() { $0 < $1 }//尾隨閉包
    nums.sort { $0 < $1 }//最后一個參數(shù)可以不用寫()
    print("nums:\(nums)")//nums:[2, 5, 6, 11, 18, 45, 68]
}
test4()

說明:

  • 閉包表達式實現(xiàn)了排序操作
  • 可以看到將閉包表達式作為了參數(shù)來傳遞
  • 這里也把前面所講的所有簡寫方式都寫了一遍

2、閉包

閉包就是可以捕獲變量/常量的函數(shù)或閉包表達式仿荆,它有兩個特點贰您,函數(shù)/閉包表達式,捕獲局部變量拢操。因此可以看到它和OC中的block基本上是一樣的了锦亦。具體關于block的認識可以看我的另一篇博客
,如果理解block令境,再看閉包就會覺得很簡單了

我們在實際理解閉包時杠园,可以將其看做一個類,捕獲的變量是成員變量舔庶,函數(shù)/閉包表達式是類的成員函數(shù)抛蚁。

2.1 閉包的使用

代碼:

/*
 1、閉包認識
 */
func test5 () {
    //定義一個函數(shù)類型
    typealias Fn = (Int) -> Int
    //返回一個函數(shù)
    func getFn() -> Fn {
        var num = 0
        //plus函數(shù)會捕獲num變量
        //plus函數(shù)和捕獲的num變量形成了閉包
        func plus(_ i: Int) -> Int {
            num += i
            return num
        }
        return plus
    }
    var fn1 = getFn()
    var fn2 = getFn()
    fn1(1) // 1
    fn2(2) // 2
    fn1(3) // 4
    fn2(4) // 6
}
test5()

說明:

  1. 可以看到此時的plus就是一個閉包惕橙,因為它本身是一個函數(shù)瞧甩,但是會捕獲外部的num變量
  2. 這里就和block一樣,num是捕獲到了堆中弥鹦,所以plus中的num和getFun()函數(shù)中的num已經不是一個地址了亲配。他們二者之間沒有關系

2.2 變量捕獲的匯編分析

閉包變量包含16個字節(jié),前8個字節(jié)是函數(shù)地址,后8個字節(jié)是變量堆空間地址

2.2.1 函數(shù)查看

代碼:

匯編分析

說明:

2.2.2 不捕獲變量的閉包

代碼:

匯編分析

說明:

2.2.3 捕獲變量的閉包

代碼:

匯編分析

說明:

2.2.4 捕獲變量的時機

代碼:

/*
 3吼虎、捕獲變量的時機
 */
func test7() {
    typealias Fn = (Int) -> Int
    func getFn() -> Fn {
        var num = 11
        func plus(_ i: Int) -> Int {
            num += i
            return num
        }
        //捕獲的是14犬钢,而非11
        num = 14
        return plus
    }
    var fn1 = getFn()
    fn1(1)//15
}

說明:

  1. 可以看到只有在返回這個plus函數(shù)地址的時候才會去捕獲,所以并不是在定義函數(shù)的時候捕獲思灰,而是在創(chuàng)建函數(shù)變量的時候捕獲
  2. 其實這里就算沒有return,只要是創(chuàng)建了plus函數(shù)對象就會捕獲的

2.3 多變量多函數(shù)的閉包

代碼:

/*
 4玷犹、多變量多函數(shù)的分析
 */
func test8() {
    func getFns() -> (Fn, Fn) {
        var num1 = 0
        var num2 = 0
        func plus(_ i: Int) -> (Int, Int) {
            num1 += i
            num2 += i << 1
            return (num1,num2)
        }
        func minus(_ i: Int) -> (Int, Int) {
            num1 -= i
            num2 -= i << 1
            return (num1,num2)
        }
        return (plus,minus)
    }
    let (p, m) = getFns()
    p(6)//(6,12)
    p(5)//(1,2)
    p(4)//(5,10)
    p(3)//(2,4)
}

說明:

  1. 多個函數(shù)捕獲同一個變量,他們捕獲后是同一份
  2. 多個變量分開alloc洒疚,是不同的堆空間
  3. 閉包我們可以將其看做一個類里歹颓。捕獲的變量是成員變量,函數(shù)是類的成員函數(shù)油湖。所以對于成員變量就是同一份
  4. 如果有多個變量巍扛,他們需要多次alloc,并不是放在同一個堆空間乏德。此時的fn變量的大小就應該更多了撤奸,如果是兩個變量,那么就是24個字節(jié)了

2.4 數(shù)組循環(huán)閉包分析

代碼:

/*
 5喊括、數(shù)組循環(huán)閉包分析
 */
func test9() {
    //定義一個數(shù)組胧瓜,元素是函數(shù)
    var functions: [() -> Int] = []
    for i in 1...3 {
        functions.append{ i }
    }
    for f in functions {
        print("\(f())")
    }
}

代碼分析:

  1. 創(chuàng)建一個函數(shù)數(shù)組([() -> Int]就表示存放函數(shù)的數(shù)組)
  2. 給數(shù)組添加三個函數(shù),每個函數(shù)的函數(shù)體都是返回一個i
    1. {}是尾隨閉包郑什,append拼接函數(shù)的參數(shù)只有這一個閉包府喳,所以就把()省略掉了。這個閉包的函數(shù)體就是返回i
  3. 之后循環(huán)執(zhí)行數(shù)組中的函數(shù)

說明:

  1. 在for循環(huán)中的閉包蘑拯,每次都會捕獲變量
  2. 也就是此時會創(chuàng)建多個堆空間

2.5 自動閉包

如果我們傳入的參數(shù)是需要作為包钝满,但是寫成包的樣式可讀性會很差,就可以設置成自動閉包申窘,系統(tǒng)幫我們閉包

代碼:

/*
 6舱沧、自動閉包
 使用關鍵字@autoclosure修飾參數(shù)
 */
func test10() {
    func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int {
        return v1 > 0 ? v1 : v2()
    }
    //下面三條語句等效
    getFirstPositive(10, 20)//自動閉包
    getFirstPositive(10, {20})
    getFirstPositive(10){20}//尾隨閉包
}
test10()

說明:

  1. 這三者是等效的,也就是說如果我再參數(shù)中加上了@autoclosure偶洋,那么在傳入參數(shù)的時候就不需要傳入閉包熟吏,直接傳入函數(shù)體后,編譯器會幫我們自動閉包
  2. 如果我們傳入的參數(shù)是需要作為包玄窝,但是寫成包的樣式可讀性會很差牵寺,就可以設置成自動閉包,系統(tǒng)幫我們閉包
  3. 本來我們需要{20}傳入閉包恩脂,但是我們傳入20后系統(tǒng)會判斷需要自動閉包就會將這個20作為閉包的函數(shù)體進行處理

注意:

  1. @autoclosure設置后會自動閉包帽氓,傳入的參數(shù)就不能是閉包。二者會沖突
  2. @autoclosure只支持() -> T格式的參數(shù)(參數(shù)為空俩块,有返回值)
  3. @autoclosure并非只只支持最后一個參數(shù)
  4. 有@autoclosure和沒有@autoclosure構成了函數(shù)重載

3黎休、總結

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末浓领,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子势腮,更是在濱河造成了極大的恐慌联贩,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捎拯,死亡現(xiàn)場離奇詭異泪幌,居然都是意外死亡,警方通過查閱死者的電腦和手機署照,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門祸泪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人建芙,你說我怎么就攤上這事没隘。” “怎么了禁荸?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵右蒲,是天一觀的道長。 經常有香客問我屡限,道長,這世上最難降的妖魔是什么炕倘? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任钧大,我火速辦了婚禮,結果婚禮上罩旋,老公的妹妹穿的比我還像新娘啊央。我一直安慰自己,他們只是感情好涨醋,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布瓜饥。 她就那樣靜靜地躺著,像睡著了一般浴骂。 火紅的嫁衣襯著肌膚如雪乓土。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天溯警,我揣著相機與錄音趣苏,去河邊找鬼。 笑死梯轻,一個胖子當著我的面吹牛食磕,可吹牛的內容都是我干的。 我是一名探鬼主播喳挑,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼持舆!你這毒婦竟也來了沼沈?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤回官,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后询张,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孙乖,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年份氧,在試婚紗的時候發(fā)現(xiàn)自己被綠了唯袄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜗帜,死狀恐怖恋拷,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情厅缺,我是刑警寧澤蔬顾,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站湘捎,受9級特大地震影響诀豁,放射性物質發(fā)生泄漏。R本人自食惡果不足惜窥妇,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一舷胜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧活翩,春花似錦烹骨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拉宗,卻和暖如春峦树,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背旦事。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工空入, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人族檬。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓歪赢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親单料。 傳聞我的和親對象是個殘疾皇子埋凯,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容