@phper
2016-09-11T16:19:25.000000Z
字数 2589
阅读 4651
swift
闭包在一般的语言中也有,而且用法也差不多,swift中也是有闭包的概念的,这一节来学习swift中的闭包。
闭包(closure),闭包在其它语言中也叫匿名函数,lambda等,实际上就一个去掉了函数名的函数体,它可以捕获和存储其所在上下文中任意常量和变量的引用。 这就是所谓的闭合并包裹着这些常量和变量,俗称闭包。
swift中有个sort
函数,可以来给数组按照自定义的函数排序。上一节讲函数的时候,也说到这个函数,我用这个例子来先说明一下闭包的概念。
//生成一个0-99的数组
var arr:[Int] = []
for i in 0..<100 {
arr.append(i)
}
// 从大到小
func sortDesc(a:Int, b:Int) -> Bool{
return a > b
}
// 从小到大
func sortAsc(a:Int, b:Int) -> Bool{
return a < b
}
//普通方式
arr.sort(sortDesc)
arr.sort(sortAsc)
//闭包方式
arr.sort({ (a:Int, b:Int) -> Bool in
return a > b
})
arr.sort({ (a:Int, b:Int) -> Bool in
return a > b
})
根据上面的一个简单的例子,我们知道来一个闭包实质上也是一个函数,所以我们总结和精简一下,如何定义一个闭包:
闭包一般是这样定义的:
{ (parameters) -> returnType in
//函数体
statements
}
闭包对比函数来说,多了一个{}
和 in
关键字。
闭包表达式语法可以使用常量
、变量
和inout类型
作为参数,不提供默认值。 也可以在参数列表的最后使用可变参数。 元组也可以作为参数和返回值。
简化的写,我们也可以写成一行,这样更简洁看起来:
arr.sort({ (a:Int, b:Int) -> Bool in return a > b })
上面的例子中sort
函数,参数是类型为(Int, Int) -> Bool的函数,因此实际Int,Int和Bool类型并不需要作为闭包表达式定义中的一部分。 因为所有的类型都可以被正确推断,返回箭头 (->) 和围绕在参数周围的括号也可以被省略:
reversed = arr.sorted({ a, b in return a > b } )
看,是不是简洁多了。
单行表达式闭包可以通过隐藏return关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为:
reversed = arr.sorted({ a, b in a > b } )
我擦,更加简洁了有没有???
Swift 自动为内联函数提供了参数名称缩写功能,您可以直接通过$0,$1,$2来顺序调用闭包的参数。
如果您在闭包表达式中使用参数名称缩写,您可以在闭包参数列表中省略对其的定义,并且对应参数名称缩写的类型会通过函数类型进行推断。 in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成:
reversed = arr.sort({ $0 > $1 } )
在上一节函数讲到高阶函数reduce
时候,直接用>
来表示一个函数
array2.reduce(0, combine: >)。
闭包中,也可以这样使用:用 >、< 来做运算,更加简洁:
arr.sort(>)
arr.sort(<)
如果将一个很长的闭包表达式作为一个参数参数一个函数,这样可读性就会很差,而且写起来很蛋疼。所以,就出现了尾随闭包,尾随闭包就是讲{}
搬到()的外面去,这样看起来可读性更高。
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
所以刚才的那个例子,也能这样写:
reversed = sorted(names) {s1, s2 in return s1 > s2}
当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。
来一个尾随闭包原始的写法:
arr.sort() {
a, b in return a > b
}
甚至,我们可以将()
也可以去掉:
arr.sort{
a, b in return a < b
}
闭包可以在其定义的上下文中捕获常量或变量
。 即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
还是sort这个例子:
// 数值捕获
var num = 300
arr.sort{ a , b in
abs(a-num) < abs(b-num)
}
我们在闭包中并没有神马这个num
值,那么在闭包中,是可以轻易捕获到的。
在swift中函数和闭包都是引用类型。
无论将函数/闭包赋值给一个常量还是变量,实际上都是将常量/变量的值设置为对应函数/闭包的引用。 值都会同事被改变。
看一个例子,定义了一个函数,参数是一天跑多少米,返回值是一个无参数,返回值是Int型的
函数。可以看的出,返回值是一个{}
,即是闭包。
//每天跑多少米,求总的跑数。
func runningMetersWithMetersPerDay( metersPerDay: Int) -> () -> Int {
var totalMeters = 0
return {
totalMeters += metersPerDay
return totalMeters
}
}
var p1 = runningMetersWithMetersPerDay(1000)
p1() //1000
p1() //2000
多次调用,发现数字在累加,而不是重新开始,说明闭包是个引用赋值的语句。
这个时候,p1是一个无参数,返回值是Int型的
函数,多次调用,其实就是在调用
{
totalMeters += metersPerDay
return totalMeters
}
这部分。totalMeters 每次都会发生变化,即是引用赋值了,而 metersPerDay 每次都会捕获 传入的参数1000来的。
我们继续什么一个p2 ,每天拍3000米:
var p2 = runningMetersWithMetersPerDay(3000)
p2() //3000
p2() //6000
var p3 = p2 //引用赋值。
p3() // 9000
可以看出。p3() 是接着累加的。进一步说明是闭包是引用赋值。