swift学习之闭包(closure)

时间:2021-02-10 22:47:11

  swift的闭包和c,oc中block是差不多的,其实swift中的全局和嵌套函数实际上也是一种特殊的闭包,闭包有三种形式:

(1)全局函数是有一个名字,但不能捕获任何值的闭包

(2)嵌套函数是有一个名字,可以捕获在函数内的值得闭包

(3)闭包表达式是一个轻量级的语法,可以从上下文中捕获值

闭包表达是一个干净,清晰,鼓励简介,优化的语法,优化包括:

(1)从上下文中推断参数和返回类型

(2)简单的闭包表达可以省略return

(3)简写参数名

(4)尾部闭包语法

(一)闭包表达

闭包表达就是以一个简明准确的方式写一个内联包

(二)sort函数

swift标准库提供了sort函数,用来排序已知类型的数组,通过你提供的排序包规则输出

        let originalNames = ["Chris","Alex","Ewa","Barry","Daniella"]
        var currentNames = originalNames.sort(backwards)
    func backwards(s1: String , s2: String) -> Bool {
        return s1 > s2
    }
这么表达有点囧长啊(long-winded),但是在这这个栗子也算比较好和合适的

下面用闭包表达一下

        var reverse = originalNames.sort({ (s1: String , s2: String) -> Bool in
            
            return s1 > s2
            
        })
解释:(1)in前面的是参数类型和返回值类型

           (2)in后面是实现部分

           (3)整个表达式用{}号括起来

由于swift有推断类型功能,闭包可以从上下文推断类型,所以上面的表达式还可以写成:

        var reverse = originalNames.sort({s1 , s2 in return s1 > s2})
        print(reverse)
另外,单行表达式可以忽略return关键字,所以上面的表达式又可以写成:

        var reverse = originalNames.sort({s1 , s2 in s1 > s2})
        print(reverse)
通过sort函数和sort的第二个参数我们就可知道这个函数一定是返回一个bool类型,返回值只有一个单一的表达式,所以return可以省略,并且不会造成歧义

(三)简写参数名

swift对于内联包自动提供了参数名的简写,这些简写名用来关联参数,那么这些看不到简写名是怎么简写的呢?就是$0,$1,$2....

如果你写闭包表达用了简写名,那你写的所有参数就可以省了,因为人家已经相当于给你提供了参数,只是简写了,就不用你写了,写了都TM浪费感情啊,不光浪费自己的,还浪费swift的,尤其是人家程序员的,我都把闭包简成这样了,你就不要写那么麻烦添乱了,省的大家都心里堵得慌,废话不多说了,下面检测一下这个简写:

        var reverse = originalNames.sort( { $0 > $1 } )
        print(reverse)

0,1这个就是从第一个参数开始关联,大家都是干程序的,知道程序排序都是从0开始吧,这个你在问为什么,我就只能呵呵了

(四)操作符(有的也叫运算符)函数

由于swift的String类型有规定两个字符的比较,并且返回的是Bool类型,所以上面我们可以写的再简单点,直接传一个操作符(运算符),swift就会自己推断调用string自身规定的实现

         var reverse = originalNames.sort(>)

(五)Trailing(尾部)闭包

  意思就是把闭包实现放在尾部来写,为什么要放在尾部呢,因为闭包实现太长了,可能会影响代码的可读性,美观性,所以就放在最后面实现好了

swift数组有一个map(映射)方法,仅仅是采用闭包表达作为参数像里面传递的。这个闭包会遍历数组的每个元素,并且返回相应的可选类型的映射。映射的核心和返回值的类型都是又闭包规定的。当使用map方法后,会返回一个新的数组, 这个数组包含了所有新的映射的值,下面举一个例子(把Int型数组转换成Strin型的数组)说明一下:

        let digitNames = [0:"Zero",1:"One",2:"Two",3:"Three",4:"Four",5:"Five",6:"Six",7:"Seven",8:"Eight",9:"Nine"]
        let numbers = [16,58,510]
        let strings = numbers.map() {
            (var number) -> String in
            var output = ""
            while number > 0 {
                output = digitNames[number % 10]! + output
                number /= 10
            }
            return output
        }
        
        print(strings)
解释:首先声明一个字典,在声明一个数组,调用map方法,自己写实现

          其次,通过return,swift已经推断strings是一个字符串型的数组了

         最后,就看打印结果了

如果闭包是函数的唯一参数,函数后面的括号可以省略,所以上面的函数还可以这么写:

        let strings2 = numbers.map {
            (var number) -> String in
            var output = ""
            while number > 0 {
                output = digitNames[number % 10]! + output
                number /= 10
            }
            return output
        }
        
        print(strings2)

在这里,number前面使用var修饰的,这个在函数有讲,就是在内部调用可变,相当于在函数内部声明了一个变量,并把number(数组的元素)传递给了这个变量,函数调用结束,他也就释放了

规定的返回类型是String类型,存储在返回的数组中了

注意:上面字典后面跟了一个感叹号,因为字典下标语法总是返回一个可选值的如果查询失败的话,但是在上面的例子中,一定要确保字典下标值是有效地,因此我们要强制拆包,得到存储在可选返回值的中的String类型值

(六)值捕获

说白了,就是闭包可以从上下文中捕获值,用为己用(变量常量都可以,还可以自己改变其值),下面举一个简单的函数嵌套的例子

        let incrementByTen = makeIncrementer(forIncrement: 10)
        print(incrementByTen())
    }

    func makeIncrementer(forIncrement amout: Int) -> Void -> Int {
        
        var runningTotal = 0
        
        func increment() -> Int {
            runningTotal += amout
            return runningTotal
        }
        
        return increment
    
    }
func increment()这个函数就捕获到了runningTotal和amout的值,makeIncrementer这个函数返回的是一个函数类型(一个不接收参数且返回值类型为Int类型的函数)
        let incrementByTen = makeIncrementer(forIncrement: 10)
        incrementByTen()
        incrementByTen()
        print(incrementByTen())
上面的打印结果猜一下是多少?这是因为闭包自动帮我们存储了变量的值,调动同一函数会自动加,如果我们再定义一个函数呢?比如下面两个的输出结果

        let incrementBySeven = makeIncrementer(forIncrement: 7)
        incrementByTen()
        print(incrementByTen())
        print(incrementBySeven())
由上面得知我们再定义一个函数闭包会引用一个新值给这个新的函数,和前面的函数不会混淆,而原始的还是执行自己的功能

注意:如果我们把一个闭包定义成了类实例的属性,通过引用这个实例或者它的成员,会产生一个强引用,不过给了我们解决办法,这个我会在后面学习之后进行讲解,谢谢

(七)闭包是引用类型

在上面的例子中,incrementByTen和incrementBySeven是常量,但是他们引用的闭包仍然可以改变runningTotal的值,这是因为函数和闭包都是引用类型的,当我们把函数或者闭包赋给一个常量或者变量时,实际上是我们用这个常量或者变量引用那个闭包,打印里面调用了也算也用,在打印下面的代码中runningTotal的值已经被+10了,

        let alsoIncrementByTen = incrementByTen
这里面的alsoIncrementByTen是incrementByTen+10的结果,不信可以自己尝试下

好啦,闭包也讲完了,下次我会介绍swift中的枚举