swift 中的闭包

闭包

闭包表达式

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)

可以这样定义一个闭包表达式

{
    (参数列表) -> 返回值类型 in
    函数体代码
}

闭包表达式的简写

定义一个方法

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

调用这个方法

完整写完是这样的

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
})

使用美元符号$ 代替参数

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

最终可以省略参数, 直接使用加号

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

尾随闭包

如果将一个很长的闭包表达式作为函数的最后一个实参, 使用尾随闭包可以增强函数可读性

尾随闭包是一个被书写在函数调用括号外面的闭包表达式

如果闭包表达式是函数的唯一实参, 而且使用了尾随闭包的语法, 就不需要再函数名后边加圆括号了

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

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

唯一实参

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

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

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

exec {
    $0 + $1
}

swift 中的数组排序sort 函数

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

sort 传入参数的简写

        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
    }

闭包

一个函数和它所捕获的变量\常量环境组合起来, 称为闭包

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, 调用函数, 则为以下结果

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的地址

恢复代码, 返回num, 则汇编为

alloc申请内存

alloc 申请一块内存, 将num 拷贝到堆中

看下rax 返回值

rax返回值
断点在21行

可以看到x/5xg 0x0000000103827eb0

第三组中为0x0000000000000001, 初始化为了1.

对应到汇编中

继续跳过一个断点, 赋值为了0x0000000000000003

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

如何看到alloc 申请了多少内存空间给fn 呢?

需要24 个字节存储数据, 但是真正给的空间为32 字节, 16 的倍数

esi存储为0x18

可以将闭包想想成一个类的实例对象

  • 内存在堆空间
  • 捕获的局部变量就是对象的成员
  • 组成闭包的函数就是类内部定义的方法
class Closure {
    var num = 0
    func plus(_ i: Int) -> Int {
        num += i
        return i
    }
}

var cs1 = Closure()
cs1.plus(1)
调用了rax,fn函数

调用到rax, 即为getFn 函数地址, jmp 为plus 地址0x100003ed0

进入到rax中

每次调用fn1, 调用的都为plus, 同一个函数地址, 第三个字节存储的是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 一次, 给两个函数共用

自动闭包

使用@autoclosure, 会将() -> T 格式的参数自动封装成闭包, 自动闭包也支持中间的参数, 有无自动闭包@autoclosure, 可以构成函数重载

例如下面代码, 如果第一个参数已经大于0, 则没必要调用第二个参数的内容

// 如果第一个数大于0, 返回第一个, 否则返回第二个
func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
    return v1 > 0 ? v1 : v2
}
getFirstPositive(10, 20)
getFirstPositive(-2, 20)
getFirstPositive(0, -4)

改写成下面, 将第二个参数修改为一个闭包函数

// 如果第一个参数已经大于0, 则第二个参数不必调用执行
func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}

在swift 中可以使用@autoclosure, 将它封装成一个自动闭包

// 可以使用自动闭包来实现
func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
    return v1 > 0 ? v1 : v2()
}
getFirstPositive(10, 20)

则在调用时, 最后一个参数自动封装成{20}

你可能感兴趣的:(swift 中的闭包)