美文网首页Swift5.1语法学习
九、闭包 闭包表达式

九、闭包 闭包表达式

作者: 爱玩游戏的iOS菜鸟 | 来源:发表于2020-01-08 16:31 被阅读0次

    闭包

    什么是闭包

    1.官方定义

    • 闭包是可以在代码中被传递引用功能型独立模块。类似于OC中的block以及其他语言的匿名函数
    • 闭包能够捕捉存储定义在其上下文中的任何常量和变量的引用
    1. MJ认为严谨的定义
    • 一个函数和它所捕获的变量/常量环境组合起来,称为闭包,其中:
      • 函数是定义在外部函数内部的内嵌函数
      • 捕获的是外层函数的局部变量/常量
    闭包的本质
    • 闭包是引用类型,可以把闭包想象成一个类的实例对象(和类进行对比,非常相似)
    • 内存在堆空间
    • 捕获的局部变量/常量就是对象的成员(存储属性)
    • 组成闭包的函数就是类内部定义的方法

    如何证明呢?汇编!

    //为什么呢?num为getFn()函数内定义的局部变量 为何num的值能够保留?
    typealias Fn = (Int) -> Int
    
    func getFn() -> Fn {
        var num = 0
        func functionPlus(_ v1:Int) ->Int{
            num += v1
            return num
        }
        return functionPlus(_:)
        
    }
    
    var fn = getFn()
    
    print(fn(1))//输出:1
    print(fn(2))//输出:3
    print(fn(3))//输出:6
    print(fn(4))//输出:10
    

    证明过程

    1. 先将funcPlus函数中使用num的代码注释 打断点查看汇编,即可看到 return返回的是funcPlus函数的地址( leaq 0xd(%rip), %rax )


      rax存放的是funcPlus的函数的地址
    2. 然后将函数还原,再调试,通过汇编查看 getFn()复杂了很多 同时发现了一个非常重要的信息 调用了swift_allocObject,也就意味着分配了一段堆空间的内存,用来存放num


      当内部函数访问了外部函数的局部变量,就会分配堆空间捕获这个变量,叫捕获 即可延长该变量的生命周期
    捕获
    • 闭包能够从上下文捕获已被定义的常量和变量。
    • 即使定义这些常量和变量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值

    继续上面的例子,如果证明分配的这一块堆空间存放的是num呢?
    在malloc下面这一条打断点 register read rax 得到堆空间的地址


    然后再将函数Funcplus 中return该行打断点 执行下一步 即fn(1)调用完毕后查看堆空间的值 即可看到该地址前16个字节 和类的实例对象分配的堆空间一样(引用类型+引用计数)接下来8个字节即为num 为 1的值,继续下一步 num的值为3 该地址中也确实为3 即证明

    补充:

    1. 在最初swiftallocObject调用完之后,访问分配的堆空间,里面为垃圾数据


      image
    2. swiftallocObject分配了多少字节?


      申请24个字节,实际分配的是32个字节(因为在堆空间是以16个字节为一组)

    闭包表达式

    闭包表达式语法(区分开闭包和闭包表达式 完全不同的两个东西)
    闭包表达式语法的一般形式
    通过闭包表达式来定义函数
    //通过闭包表达式来定义函数
    var fn = {
        (v1:Int, v2:Int) -> Int in
        v1 + v2
    }
    
    fn(10,20)
    
    闭包表达式的简写
    //Exec函数
    func Exec(v1:Int, v2:Int, fn:(Int, Int) -> Int) {
        print(fn(v1,v2))
    }
    
    func Sum(_ v1 :Int, _ v2 :Int ) -> Int {
        v1 + v2
    }
    
    //Sum函数作为Exec(v1:v2:fn:)的形参传入
    Exec(v1: 10, v2: 20, fn: Sum(_:_:))
    
    //闭包表达式 单表达式隐式返回
    Exec(v1: 10, v2: 20, fn: {(v1:Int, v2:Int) -> Int in
        v1 + v2
    })
    
    //尾随闭包  后面具体讲述
    Exec(v1: 10, v2: 20){(v1:Int, v2:Int) -> Int in
        v1 + v2
    }
    
    //从语境推断类型
    //因为形参类型以及返回值类型能通过语境中确定 可以省略()、->、形参、返回值类型
    Exec(v1: 10, v2: 20, fn: {v1, v2 in v1 + v2})
    
    //尾随闭包
    Exec(v1: 10, v2: 20){
        v1, v2 in v1 + v2
    }
    
    //简写实际参数名
    Exec(v1: 10, v2: 20, fn: {$0 + $1})
    
    //尾随闭包
    Exec(v1: 10, v2: 20){
        $0 + $1
    }
    
    //运算符函数
    Exec(v1: 10, v2: 20, fn: +)
    
    Exec(v1: 10, v2: 20){ + } //报错 不能这么写 此"+"被判定为一元运算符正号 原因未知
    
    尾随闭包
    • 将很长的闭包表达式作为函数最后一个实参时,使用尾随闭包,增强函数可读性
    • 尾随闭包是一个被写在函数调用括号后面的闭包表达式 (见上栗)
    • 闭包表达式是函数的唯一实参,而且使用了尾随闭包的语法,那就不需要再函数后面写圆括号
    //Exec函数
    func Exec(fn:(Int, Int) -> Int) {
        print(fn(11,2))
    }
    
    Exec(fn: {$0 + $1})
    Exec(){ $0 + $1 }
    Exec{ $0 + $1 }
    

    示例: 数组排序sorted:

    var name = ["abc", "adc", "abd", "bac"]
    
    func backward(_ s1:String, _ s2:String) -> Bool {
        return s1 < s2
    }
    
    //函数作为sorted(by:)
    var reveredNames = name.sorted(by: backward(_:_:))
    
    //闭包表达式 隐式返回
    var revered2Names = name.sorted(by: {(s1 :String, s2 :String) -> Bool in
        return s1 < s2
    })
    
    //var revered2Names = name.sorted { (s1 :String, s2 :String) -> Bool in
    //    return s1 < s2
    //}//尾随闭包
    
    //从语境推断类型
    //因为形参类型以及返回值类型能通过语境中确定 可以省略()、->、形参、返回值类型
    var revered3Names = name.sorted(by: { s1, s2 -> Bool in
        s1 < s2
    })
    
    //var revered3Names = name.sorted { s1, s2 -> Bool in
    //    s1 < s2
    //}//尾随闭包
    
    //简写实际参数名
    var revered4Names = name.sorted(by: { $0 < $1})
    //var revered4Names = name.sorted { $0 < $1}//尾随闭包
    
    //运算符函数
    var revered5Names = name.sorted(by: < )
    

    作业:

    1. 上面的例子 如果num为全局变量呢?


      不符合闭包的定义,捕获的是外部函数的局部变量

    (2)题目2,分配的堆空间的大小是多少?

    image

    相关文章

      网友评论

        本文标题:九、闭包 闭包表达式

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