美文网首页swift
swift 中的闭包

swift 中的闭包

作者: freemanIT | 来源:发表于2022-05-10 18:36 被阅读0次

    闭包

    闭包表达式

    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 中的闭包

        本文链接:https://www.haomeiwen.com/subject/lwtrurtx.html