swift 中的閉包

閉包

閉包表達(dá)式

var fn = {
    (v1:  Int, v2: Int) -> Int in
    return v1 + v2
}
fn(10, 20)

{
    (v1: Int, v2: Int) -> Int in
    return v1 + v2
}(10, 20)

可以這樣定義一個(gè)閉包表達(dá)式

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

閉包表達(dá)式的簡寫

定義一個(gè)方法

func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
    print(fn(v1, v2))
}

調(diào)用這個(gè)方法

完整寫完是這樣的

exec(v1: 10, v2: 20, fn: {
    (v1: Int, v2: Int) -> Int in
    return v1 + v2
})

可以將類型省略

exec(v1: 10, v2: 20, fn: {
    v1, v2 in return v1 + v2
})

也可以將return 省略

exec(v1: 10, v2: 20, fn: {
    v1, v2 in v1 + v2
})

使用美元符號(hào)$ 代替參數(shù)

exec(v1: 10, v2: 20, fn: {$0 + $1})

最終可以省略參數(shù), 直接使用加號(hào)

exec(v1: 10, v2: 20, fn: +)

尾隨閉包

如果將一個(gè)很長的閉包表達(dá)式作為函數(shù)的最后一個(gè)實(shí)參, 使用尾隨閉包可以增強(qiáng)函數(shù)可讀性

尾隨閉包是一個(gè)被書寫在函數(shù)調(diào)用括號(hào)外面的閉包表達(dá)式

如果閉包表達(dá)式是函數(shù)的唯一實(shí)參, 而且使用了尾隨閉包的語法, 就不需要再函數(shù)名后邊加圓括號(hào)了

exec(v1: 10, v2: 20) { v1, v2 in
    v1 + v2
}

exec(v1: 10, v2: 20) { $0 + $1 }

唯一實(shí)參

func exec(fn: (Int, Int) -> Int) {
    print(fn(1, 2))
}

exec(fn: {
    $0 + $1
})

exec(fn: {
    $0 + $1
})

exec {
    $0 + $1
}

swift 中的數(shù)組排序sort 函數(shù)

@inlinable public func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
        let nums = [11, 2, 6, 5, 18, 45, 99, 65]
    // 自己定一個(gè)比較
    func cmp(i1: Int, i2: Int) -> Bool {
        // 值比較大的放在左邊
        return i1 > i2
    }
    
    let arr = nums.sorted(by: cmp(i1:i2:))
    print("\(#line) ~~~~~~ \(#function) ~~~~~~ \(arr)")

sort 傳入?yún)?shù)的簡寫

        nums.sorted(by:{
        (i1: Int, i2: Int) -> Bool in
        return i1 > i2
    })
    
    nums.sorted(by: {
        i1, i2 in
        i1 > i2
    })
    
    nums.sorted(by: {
        $0 > $1
    })
    
    nums.sorted(by: >)
    
    nums.sorted() {
        $0 > $1
    }

        nums.sorted {
        $0 > $1
    }

閉包

一個(gè)函數(shù)和它所捕獲的變量\常量環(huán)境組合起來, 稱為閉包

func getFn() -> (Int) -> Int {
    var num = 0
    func plus(_ i: Int) -> Int {
        num += i
        return num
    }
    return plus(_:)
}

var fn1 = getFn()
fn1(1)
fn1(2)
fn1(3)
fn1(4)
// 1 3 6 10

fn 捕獲了變量num

如果不捕獲num, 直接返回i, 調(diào)用函數(shù), 則為以下結(jié)果

func getFn() -> (Int) -> Int {
    var num = 0
    func plus(_ i: Int) -> Int {
        return i
    }
    return plus(_:)
}

rax 通常保存返回值

讀取rax 值

register read rax
     rax = 0x0000000100003df0  TestSwift`plus(Swift.Int) -> Swift.Int at main.swift:19
fn的地址

恢復(fù)代碼, 返回num, 則匯編為

alloc申請內(nèi)存

alloc 申請一塊內(nèi)存, 將num 拷貝到堆中

看下rax 返回值

rax返回值
斷點(diǎn)在21行

可以看到x/5xg 0x0000000103827eb0

第三組中為0x0000000000000001, 初始化為了1.

對應(yīng)到匯編中

繼續(xù)跳過一個(gè)斷點(diǎn), 賦值為了0x0000000000000003

(lldb) x/5xg 0x0000000103827eb0
0x103827eb0: 0x0000000100004018 0x0000000200000003
0x103827ec0: 0x0000000000000003 0x0000000000000000
0x103827ed0: 0x0000000000000000

如何看到alloc 申請了多少內(nèi)存空間給fn 呢?

需要24 個(gè)字節(jié)存儲(chǔ)數(shù)據(jù), 但是真正給的空間為32 字節(jié), 16 的倍數(shù)

esi存儲(chǔ)為0x18

可以將閉包想想成一個(gè)類的實(shí)例對象

  • 內(nèi)存在堆空間
  • 捕獲的局部變量就是對象的成員
  • 組成閉包的函數(shù)就是類內(nèi)部定義的方法
class Closure {
    var num = 0
    func plus(_ i: Int) -> Int {
        num += i
        return i
    }
}

var cs1 = Closure()
cs1.plus(1)
調(diào)用了rax,fn函數(shù)

調(diào)用到rax, 即為getFn 函數(shù)地址, jmp 為plus 地址0x100003ed0

進(jìn)入到rax中

每次調(diào)用fn1, 調(diào)用的都為plus, 同一個(gè)函數(shù)地址, 第三個(gè)字節(jié)存儲(chǔ)的是num 的地址

如果num 為全局變量, 則

沒有捕獲num

如果如下代碼

typealias Fn = (Int) -> (Int, Int)
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)
m(5) // (1, 10)
p(4) // (5, 18)
m(3) // (2, 17)

num1num2, 分別alloc 一次, 給兩個(gè)函數(shù)共用

自動(dòng)閉包

使用@autoclosure, 會(huì)將() -> T 格式的參數(shù)自動(dòng)封裝成閉包, 自動(dòng)閉包也支持中間的參數(shù), 有無自動(dòng)閉包@autoclosure, 可以構(gòu)成函數(shù)重載

例如下面代碼, 如果第一個(gè)參數(shù)已經(jīng)大于0, 則沒必要調(diào)用第二個(gè)參數(shù)的內(nèi)容

// 如果第一個(gè)數(shù)大于0, 返回第一個(gè), 否則返回第二個(gè)
func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
    return v1 > 0 ? v1 : v2
}
getFirstPositive(10, 20)
getFirstPositive(-2, 20)
getFirstPositive(0, -4)

改寫成下面, 將第二個(gè)參數(shù)修改為一個(gè)閉包函數(shù)

// 如果第一個(gè)參數(shù)已經(jīng)大于0, 則第二個(gè)參數(shù)不必調(diào)用執(zhí)行
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}

在swift 中可以使用@autoclosure, 將它封裝成一個(gè)自動(dòng)閉包

// 可以使用自動(dòng)閉包來實(shí)現(xiàn)
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)

則在調(diào)用時(shí), 最后一個(gè)參數(shù)自動(dòng)封裝成{20}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 閉包和閉包表達(dá)式 閉包是可以在你的代碼中杯傳遞和引用的功能性獨(dú)立代碼塊 閉包能夠捕獲和儲(chǔ)存定義再起上下文中任何常量...
    孤雁_南飛閱讀 448評論 0 0
  • 1. 閉包表達(dá)式語法 下面是利用閉包對數(shù)組進(jìn)行反向排序的一個(gè)例子: 2. 根據(jù)上下文推斷類型 上文中的參數(shù)類型和返...
    keisme閱讀 425評論 0 0
  • 閉包是自包含的函數(shù)代碼塊,Swift 中的閉包與 C 和 Objective-C 中的代碼塊(blocks)以及其...
    afluy閱讀 1,295評論 0 5
  • 前言:最近在自學(xué)swift,其中swift的閉包其實(shí)就是類似于oc中的block,只是兩者還是有很多區(qū)別的,這里做...
    Code_Caty閱讀 5,708評論 0 1
  • 閉包是自包含的函數(shù)代碼塊,可以在代碼中被傳遞和使用。Swift 中的閉包與 C 和 Objective-C 中的代...
    CodingIran閱讀 374評論 0 0

友情鏈接更多精彩內(nèi)容