美文网首页
Swift-闭包坑

Swift-闭包坑

作者: 修_远 | 来源:发表于2017-08-31 20:02 被阅读30次

    先看一篇关于OC的block和Swift的闭包对比的介绍
    在oc中主推的blockSwift中变成了闭包,先来看看Swift闭包的基本用法吧


    关于block的介绍

    Block就是一个代码块,但是它的神奇之处在于在内联(inline)执行的时候(这和C++很像)还可以传递参数。同时block本身也可以被作为参数在方法和函数间传递,这就给予了block无限的可能。

    第一个区别,函数指针是对一个函数地址的引用,这个函数在编译的时候就已经确定了。而block是一个函数对象,是在程序运行过程中产生的。在一个作用域中生成的block对象分配在栈(stack)上,和其他所有分配在栈上的对象一样,离开这个作用域,就不存在了。
    Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观。


    1. 闭包的概念和分类

    • 自闭包的函数代码块
    • 全局闭包(有名)
    • 闭包表达式(匿名)-- 能捕获上下文中的常量和变量
    • 嵌套函数(*)

    2. 闭包表达式的语法

    函数: func funcName(参数列表) ->返回值类型 {}
    闭包表达式: {} 是匿名的, 有参数列表和返回值类型

    闭包是一个使用花括号{} 包围起来,并且使用函数类型()->()来定义的代码模块。->符号分割了输入参数和返回值类型。在关键字 in 来区分闭包的头和闭包函数体。

    格式:

    {  
      (参数列表) ->返回值类型 in
                         执行语句
    }
    

    先看一个闭包的最简单例子

    定义一个常量sayHello, 类型是 {}闭包表达式类型, 这种将闭包赋值给一个常量的方法用的很少

    let sayHello = {
        print("hello World!")
    }
    //调用闭包
    sayHello()
    

    调用的方式和函数的调用方式类似

    再来看一个带参数的闭包

    let add = { (a: Int, b: Int) ->Int in
        return a + b
    }
    /*<=>
        let add: (Int, Int) ->Int = { (a: Int, b: Int) ->Int in
            return a + b
        }
     */
    print(add(2, 3))
    

    3. 闭包表达式的调用

    创建并输出一个测试数组

    var array = [25, 43, 1, 53, 54]
    var printCtn = 0
    func showArray() {
        printCtn += 1
        print("第\(printCtn)次输出>>>\t\(array)")
    }
    

    从小到大排列

    func bubbleSort(_ array: inout [Int]) {
        let cnt = array.count
        for i in 0 ..< cnt {
            for j in i + 1 ..< cnt {
                if (array[i] > array[j]){
                    let t = array[i]
                    array[i] = array[j]
                    array[j] = t
                }
            }
        }
    }
    

    如果我们不想从小到大排列,想要从大到小排列或者是按照个位数最小的排列,上面的一段代码就不能用了,又要重新写一个排列的函数,这样肯定是不合理的,那么我们用的方式就是添加一个参数,用来表示排序策略。

    我们可以加一个cmp参数表示排序策略,

    • -1 表示降序
    • 0 表示乱序
    • 1 表示升序
    func bubbleSort(_ array: inout [Int], cmp: int) {
        let cnt = array.count
        for i in 0 ..< cnt {
            for j in i + 1 ..< cnt {
                    switch: cmp
                    {
                        //...这里省略实现过程,因为对这种方法不做介绍
                    }
                }
            }
        }
    }
    

    很显然,这样也只能表示三种排序方式,而且每一种方式都要对应一个实现的代码块,这种方式不可取,下面介绍用闭包做参数来实现的方法

    先看代码

    func bubbleSort1(_ array: inout [Int], cmp: (Int, Int) ->Int) {
        let cnt = array.count
        for i in 0 ..< cnt {
            for j in i + 1 ..< cnt {
                if (cmp(array[i], array[j]) == -1){
                    let t = array[i]
                    array[i] = array[j]
                    array[j] = t
                }
            }
        }
    }
    

    这段代码里面我们看不到到底是怎么排列的,只知道当cmp(Int,Int) == -1的时候交换,很显然,这个就是排序策略,下面来看看比较策略的实现

    实现比较策略

    • 实现从大到小排列的策略
    let upCmp = {
        (i: Int, j : Int) -> Int in
        return i < j ? -1 : (i==j ? 0 : 1)
    }
    showArray()
    bubbleSort1(&array, cmp: upCmp)
    showArray()
    
    • 实现从小到大排列的策略
    let downCmp = {
        (i: Int, j : Int) -> Int in
        return i > j ? -1 : (i==j ? 0 : 1)
    }
    showArray()
    bubbleSort1(&array, cmp: downCmp)
    showArray()
    
    • 实现按个位数从小到大排列
    let intCmp = {
        (i: Int, j: Int) ->Int in
        return i%10 > j%10 ? -1 : (i%10 == j%10 ? 0 : 1)
    }
    showArray()
    bubbleSort1(&array, cmp: intCmp)
    showArray()
    

    4. 闭包表达式的优化

    优化一:在调用函数时,闭包中的参数不给类型,有函数中的闭包的参数去推断参数类型

    showArray()
    bubbleSort1(&array,cmp: {
        (i, j) ->Int in     //(i, j)不给类型,让类型推断去推断类型
        return i%10 > j%10 ? -1 : (i%10 == j%10 ? 0 : 1)
    })
    showArray()
    

    优化二 :省略参数名和返回值类型, 用$0, $1来表示参数列表中的参数, 从左到右依次是%0, %1, %2, ...

    bubbleSort3(&array,cmp: {
        return $0%10 > $1%10 ? -1 : ($0%10 == $1%10 ? 0 : 1)
    })
    showArray()
    

    一般规范: 为了书写方便, 往往将闭包表达式作为函数形参的最后一个参数书写

    func bubbleSort3(_ array: inout [Int], cmp: (Int, Int) ->Int) {
        let cnt = array.count
        for i in 0 ..< cnt {
            for j in i + 1 ..< cnt {
                if (cmp(array[i], array[j]) == -1){
                    let t = array[i]
                    array[i] = array[j]
                    array[j] = t
                }
            }
        }
    }
    

    5. 尾随闭包

    省略 return: 当闭包中只有一条语句的时候,可以省略 return

    bubbleSort1(&array, cmp: {
        $0 < $1 ? -1 : ($0 == $1 ? 0 : 1)
    })
    showArray()
    

    当闭包作为函数的最后一个参数时,可以把闭包的实现放在参数列表外面

    bubbleSort1(&array) {$0 > $1 ? -1 : ($0 == $1 ? 0 : 1)}
    showArray()
    

    6. 嵌套函数

    func bubbleSortFunction(_ array: inout [Int]) {
        let cnt = array.count
        sortBubble(&array, cnt: cnt)
        
    }
    func sortBubble(_ array: inout [Int], cnt: Int) {
        for i in 0 ..< cnt {
            for j in i + 1 ..< cnt {
                if (array[i] > array[j]){
                    swapValue(&array[i], b: &array[j])
                }
            }
        }
    }
    func swapValue(_ a: inout Int, b: inout Int) {
        let t = a
        a = b
        b = t
    }
    showArray()
    bubbleSortFunction(&array)
    showArray()
    

    7. 闭包值捕获

    swift中,可以捕获值的闭包的最简单形式是嵌套函数。也就是定义在其他函数体内部的函数嵌套函数可以捕获其外部函数所有的参数以及定义的变量或者常量。

    func getIncFunc(_ inc: Int) ->(Int) ->Int {
        var mt = 10
        print("\nmt in getIncFunc ==== \(mt)")
        func incFunc(_ v: Int) ->Int {
            mt += 1
            print("mt in incFunc ==== \(mt)")
            return inc + v + mt
        }
        
        return incFunc
    }
    let incFunc1 = getIncFunc(3)
    //调用getIncFunc(3)之后, getIncFunc()方法中的变量inc本应该被销毁, 但是被方法incFunc()保留了, 具体的参考函数的管理方式--栈的管理方式
    print("incFunc1 = \(incFunc1)")
    print("incFunc1 = \(incFunc1(10))")
    print("incFunc1 = \(incFunc1(10))")
    print("incFunc1 = \(incFunc1(10))\n")
    

    输出为

    mt in getIncFunc ==== 10
    incFunc1 = (Function)
    mt in incFunc ==== 11
    incFunc1 = 24
    mt in incFunc ==== 12
    incFunc1 = 25
    mt in incFunc ==== 13
    incFunc1 = 26
    

    GitHub传送卷轴

    相关文章

      网友评论

          本文标题:Swift-闭包坑

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