美文网首页
认识Swift系列7之闭包

认识Swift系列7之闭包

作者: Niuszeng | 来源:发表于2019-07-11 19:06 被阅读0次
    // 1.闭包表达式 Closure expression
    func test_closure_expression() {
        // 闭包的定义
        /**
         用in来分开闭包类型和语句块代码
         {
            (参数...)->返回值类型 in
            闭包代码
         }
         */
        
        
        // 定义一个函数
        func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2 }
        // 定义一个闭包表达式
        var closure = {
            (v1: Int, v2: Int)->Int in return v1 + v2
        }
        
        // 注意:虽然定义闭包的时候写了参数标签,但是调用的时候不写标签
//        print("Closure expression \(sum(1, 2))")
//        print("Closure expression \(closure(1, 2))")
        
        // 匿名闭包
//        {
//            (v1: Int, v2: Int)->Void in
//            print("v1 + v2 = \(v1 + v2)")
//        }(1, 2)
        
        func exec(fn:(Int, Int)->Int){}
        exec { _ , _ in 10} // 当不需要用到参数时,可以使用_忽略
    }
    test_closure_expression()
    
    // 2.闭包表达式的简化
    func test_closure_simplify() {
        func exec(v1: Int, v2: Int, sum: (Int, Int)->Int) {
            print("closure_simplify->exec:\(sum(v1, v2))")
        }
        
        // 正常调用
        exec(v1: 1, v2: 2, sum: { (x1: Int, x2: Int) -> Int in
            return x1 + x2
        })
        
        // 注意:以下一些列省略系编译器自动推断技术
        // 简化1
        // (由于exec函数已经明确需要两个Int类型参数,因此此处可以省略参数类型和小括号)
        //  (由于exec函数已经明确返回Int类型的参数,因此可省略返回值类型)
        exec(v1: 1, v2: 2, sum: { x1, x2 in
            return x1 + x2
        })
        
        // 简化2(单条语句,简化掉return)
        exec(v1: 1, v2: 2, sum: { x1, x2 in x1 + x2 })
        
        // 简化3(可以省略参数列表和in)
        // 使用美元符$+数字表示第几个参数
        exec(v1: 1, v2: 2, sum: { $0 + $1 })
        
        // 简化4-1(只有两个参数,直接写个+号,编译器会自动推断出是加法运算)
        exec(v1: 1, v2: 2, sum: + ) // 两个参数才可以
        
        // 简化4-2(尾随闭包)
        exec(v1: 1, v2: 2) {$0 + $1}
        
        
        // Test ...
        func exec1(v1: Int, v2: Int, v3: Int, sum: (Int, Int, Int)->Int) {
            print("closure_simplify->exec:\(sum(v1, v2, v3))")
        }
        exec1(v1: 1, v2: 2, v3: 3, sum: {$0 + $1 + $2})
        exec1(v1: 1, v2: 2, v3: 3) {$0 + $1 + $2}
    }
    test_closure_simplify()
    
    // 3.尾随闭包
    func test_tail_closure() {
        // 将闭包作为函数的最后一个参数,可以将该闭包写到小括号外,类似函数体,增强可读性
        func exec(v1: Int, v2: Int, v3: Int, sum: (Int, Int, Int)->Int) {
            print("test_tail_closure->exec:\(sum(v1, v2, v3))")
        }
        exec(v1: 1, v2: 2, v3: 3, sum: {$0 + $1 + $2} )
        exec(v1: 1, v2: 2, v3: 3) { $0 + $1 + $2 } // 尾随
        
        func exec1(fn: (Int, Int, Int)->Int) {
            print("test_tail_closure->exec1:\(fn(1,2,3))")
        }
        exec1(fn: {$0 + $1 + $2}) // 正常调用
        exec1() {$0 + $1 + $2} // 尾随1
        exec1 {$0 + $1 + $2} // 尾随2,省略小括号
    }
    test_tail_closure()
    
    // 4.闭包值捕获
    func test_closure_capturing_values() {
        // 将闭包作为函数的最后一个参数,可以将该闭包写到小括号外,类似函数体,增强可读性
        typealias Fn = (Int)->Int
        func getFn()->Fn {
            var num = 0
            func plus(_ i: Int)->Int {
                num += i
                return num
            }
            return plus
        }
        
        let fn = getFn()
        fn(4) // 4
        fn(1) // 5
        
        /** 注意:这里fn其实就是内部的plus
         由于num实际上是getFn栈空间的一个局部变量,当 let fn = getFn() 完成后,getFn栈空间会销毁
         但是fn使用num的时候,getFn栈空间已经销毁,因此为了保存num的值,fn(plus)会将num的值捕获
         为了要捕获num的值,闭包的实现方式将从函数升级为对象
         
         这里的底层实现实际上是在堆空间创建了一个 fn对象,fn的内存结构如下
            fn堆空间内存
         ┏━━━━━━━━━━━━━━━━━━━━━━┓
         ┃   classInfo          ┃
         ┃  retainCount         ┃
         ┃    var num           ┃
         ┃  plus(Int)->Int{...} ┃
         ┗━━━━━━━━━━━━━━━━━━━━━━┛
         
         即fn拥有一个成员变量 num 和 一个方法plus,即fn底层实现是使用了对象来实现
         
         当fn中没有捕获外层函数栈空间的局部变量时,不会发生值捕获
         当fn内部需要操作的值是全局变量时,也不会发生值捕获
         fn发生值捕获的唯一条件就是要捕获的值声明周期过期的情况(一半在函数栈空间的局部变量)
         */
    }
    test_closure_capturing_values()
    
    // 5.自动闭包
    func test_auto_closure() {
        // 一个需求:如果第一个数乘5大于20,返回第一个,否则返回第二个乘5
        
        // 使用如下函数实现
        func getFirstMoreThan20(_ v1: Int, _ v2: Int)->Int {
            return v1 > 20 ? v1 : v2
        }
        
        let a = 5
        let b = 6
        getFirstMoreThan20(a * 5, b * 5)
        /** 分析
         在函数 getFirstMoreThan20 中,通过第一步发现v1(a*5)大于20.直接返回
         此时返现第二个参数 b*5成了无效操作,因此试想内否让b*5延迟执行
         即只有当a*5不满足条件时才会发生b*5操作,这无疑会提升时间性能
         */
        
        // 改造函数1
        func getFirstMoreThan20_1(_ v1: Int, _ v2: ()->Int)->Int {
            return v1 > 20 ? v1 : v2()
        }
        
        // 此时的调用变成了这样,能满足我们说的v2延迟执行,即v1满足了条件,v2的b*5不会发生
        // 不过该做法看起来不够优雅,还必须要求我们写{},swift提供的自动闭包技术可以优化该操作
        getFirstMoreThan20_1(a * 5, {b * 5})
        getFirstMoreThan20_1(a * 5) {b * 5}
        
        
        // 改造函数2(在v2闭包之前添加@autoclosure关键字)
        func getFirstMoreThan20_2(_ v1: Int, _ v2: @autoclosure ()->Int)->Int {
            return v1 > 20 ? v1 : v2()
        }
        
        // 此时调用变得和普通函数没有区别,也看不出 b * 5 会延迟执行
        // 但实际上是隐形优化,对外暴露的接口很友好,内部确做了优化操作
        // 该操作相当于@autoclosure会在编译阶段自动包装代码,过程如下
        // b * 5 --> {b * 5} --> {()->Int in b * 5}
        // 这就是自动闭包的优雅之处
        getFirstMoreThan20_2(a * 5, b * 5)
    }
    test_auto_closure()

相关文章

网友评论

      本文标题:认识Swift系列7之闭包

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