【Swift学习】闭包(Closure)

闭包(Closure)是可以在代码中被传递和引用的功能性独立模块。闭包功能非常强大,你可以将闭包赋值给一个变量,也可以将闭包作为一个函数参数,甚至可以将比较作为一个函数的返回值。闭包还能够捕获和存储其所在上下文中任意常量和变量的引用,这也就是所谓的闭合并包裹着常量和变量,因此被称为“闭包”。



闭包基础


闭包变量的声明


闭包就是匿名函数,我们可以定义一个闭包变量,其实就是定义一个特定函数类型的变量,方式如下:

var multiplyClosure: (Int, Int) -> Int


可以看到该闭包有两个输入参数,一个Int类型返回值。这与函数的变量声明完全相同,就像之前所说,闭包是一个没有名字的匿名函数,闭包类型其实就是函数类型。



定义一个闭包


{ (parameters) -> returnType in
     statements
}

闭包是用花括号{}包围起来,并使用函数类型()->()来定义的代码模块。‘->’符号分隔了输入参数和返回值类型,关键字in表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始,即区分了闭包的头和闭包函数体。


我们可以定义一个闭包赋值给刚刚声明的闭包变量:

multiplyClosure = { (a: Int, b: Int) -> Int in
    return a * b
}
其实这与函数的声明非常类似,都有参数列表、->、返回值以及函数体,区别就是闭包将所有的信息都放在{}里面,且在返回值后面多了关键字in。



闭包的使用


定义好闭包之后,就可以向使用函数一样使用闭包了:

let result = multiplyClosure(4,2)   //8




简明扼要的闭包表达式


来看下面这个函数:

func operateOnNumbers(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) ->Int {
    let result = operation(a, b)
    return result
}

定义了一个名为operateOnNumbers的函数,含有三个参数分别是两个Int值和一个函数类型,且operateOnNumbers返回值是Int。


我们可以直接在函数调用中定义闭包:

operateOnNumbers(4, 2, operation: {(a: Int, b: Int) -> Int in return a+b})


与函数相比,闭包是被设计成轻量级的,有很多方式可以简化闭包的语法,来看看我们可以如何简化:


1.如果闭包汇总的函数体只有一条return语句,我们可以省略return:

operateOnNumbers(4, 2, operation: {(a: Int, b: Int) -> Int in a+b})


2.我们知道Swift有类型推断的特性,因此可以省略参数类型及返回值类型:

operateOnNumbers(4, 2, operation: {(a, b) in  a+b})


3.我们甚至可以直接省略参数列表,Swift允许我们用数字引用参数:

operateOnNumbers(4, 2, operation: {$0 + $1 })


4.事实上我们还可以进一步简化,由于运算符+是一个函数,且有两个参数并非返回一个结果,因此可以只保留运算符+:

operateOnNumbers(4, 2, operation: +)


5.当函数类型是参数列表的最后一个参数时,我们将闭包写在{}中:

operateOnNumbers(4, 2) {
    $0 + $1
}




闭包捕获


闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。


来看个例子:

func countingClosure() -> (() -> Int ){
    var counter = 0
    let incrementCounter: () -> Int = {
        counter += 1
        return counter
    }
    return incrementCounter
}

定义了一个函数,没有参数并返回类型是一个闭包,这个闭包也没有参数且返回类型为Int。


下面来调用这个函数:

let counter1 = countingClosure()
let counter2 = countingClosure()

声明了两个常量,这两个常量都是闭包类型的变量,因为函数countingClosure返回的是一个闭包。


我们可以直接通过常量来调用这个闭包:

counter1()      //1
counter2()      //1
counter1()      //2
counter1()      //3
counter2()      //2

这个时候就体现闭包的强大之处了。因为闭包返回的是counter的值,而我们知道在函数内部定义的变量当函数执行完成后将会释放该变量,也就是说,当声明counter1、counter2两个常量时,函数已经执行完毕了,也就是已经释放相关的资源,按理来说counter变量应该也已经被释放了,为什么还可以使用闭包对它进行加法运算呢?


虽然counter变量是在闭包之外声明,但只要在闭包内部使用都会默认被闭包保留引用,这是为了确保闭包执行时,变量还活着。所以当函数执行完毕需要释放资源时,系统检测counter变量仍被引用,因此不会被释放,这样闭包就可以正常执行了。


至于counter值的变化原因就更加简单了,常量counter1、counter2声明的同时创建了两个counter变量,由闭包对counter变量进行引用,所以两个counter变量是互不印象的。调用counter1()只改变了counter1所对应的counter变量,而counter2所对应的counter变量不变。虽然有点拗口,但是相信还是非常容易理解的。


关于闭包捕获的原理,http://swift.gg/2016/09/09/closure-capture-1/ 此网站有详细的介绍,此处就不在赘述了




使用闭包迭代集合


Swift的集合中自带了一些比较好用的闭包函数,如Map、Filter、Reduce。接下来就来看看这些闭包的功能吧



Filter(过滤器)


Filer的用法还是比较好理解的,就是过滤掉不符合条件的数据,并生成一个新的集合,新的集合中存放的是符合条件的数据,原集合不变。


还是来看例子吧:

var prices = [1.5, 10, 4.99, 2.30, 8.19]

//过滤不大于5的数据,返回大于5的数据
let largePrices = prices.filter {     //[10, 8.19]
    return $0 > 5
}

prices     //[1.5, 10, 4.99, 2.30, 8.19],  原集合保持不变



Map(映射)


先看下面这段代码:

let salePrices = prices.map {   //[1.35, 9.0, 4.491, 2.07, 7.371]
    return $0 * 0.9
}


通过上面的代码段及运行结果不难看出,map闭包的功能就是对集合中的元素进行遍历,然后通过映射规则对元素进行处理,最后返回的是处理后的新数组,原数组保持不变。



Reduce


Reduce用来对集合进行合并,并返回合并后的值。


let stock = [1.5:5, 10:2, 4.99:20, 2.30:5, 8.19:30]
let stockSum = stock.reduce(0) {    //384.5
    return $0 + $1.key * Double($1.value)
}


Reduce闭包有两个参数,当前的值(current value)以及集合中参数的值,当前值默认为0。上方代码的功能是算出字典中key和value的乘积总和。于是参数中current value就是乘积总和,初始值为0,用$0表示;第二个参数就是字典中的元素,用$1表示,用$1.key和$1.value分别调用元素的key和value值。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值