您的位置:首页 > 移动开发 > Swift

闭包—Swift学习笔记(十)

2014-06-22 17:42 267 查看
注:本文为自己学习The Swift Programming Language的笔记,其中的例子为引用原书和其他博文或自己原创的。每个例子都会批注一些实践过程中的经验或思考总结。
1.基础
闭包[Closure]是在代码中可以被传递和使用的功能模块,类似于C语言和Objective-C的block以及其他语言的lambda表达式。
闭包可以捕获和储存上下文之间任意常量和变量的引用,就像把变量和常量包裹在一起,因而得名[闭包]。Swift会管理所有捕获所需要的内存,对于捕获的概念将在后面的章节中详细阐述。
函数是一种特殊的闭包,事实上闭包有下列三种形式:
(1)全局函数是有名字但不捕获任何值的闭包
(2)嵌套函数是有名字并且能捕获其作用函数域内的值的闭包
(3)闭包表达式是没有名字的用轻量级语法书写的可以捕捉上下文出现的值的闭包
闭包表达式是一种简洁清晰的表达,并鼓励在某些场景中进行简洁的优化优化,这些优化主要包括:
(1)从上下文中推断参数和返回值类型
(2)单表达[single-expression]闭包省略return语句
(3)缩写参数名
(4)trailing闭包语法
2.闭包表达式

闭包表达式是一种利用简洁语法构建内联闭包的方式,它提供了在不失清晰性和意图性的前提条件下书写最简洁表达式的语法优化。
下面的闭包表达式的例子通过几次迭代展示sort函数定义和语法优化形式,每一次优化都在实现相同功能的情况下做到更加简洁。
假设sort函数将字符串数组逆序排序,初始字符串为:

let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

全局函数sort有两个参数,一个是同一类型的数组(这里是字符串数组),另一个是(argumentType, argumentType) -> Bool类型(这里是(String, String) -> Bool类型)的排序闭包,第一个参数值若应该在第二个参数值前面时排序闭包返回true,否则返回false。

下面是实现这个sort排序的不同方法:

2.1函数参数方式
backwards全局函数作为闭包参数传入sort函数中:
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = sort(names, backwards)
for item in reversed {
println(item)
}

2.2完整闭包表达式方式

闭包表达式语法可以使用常量参数和变量参数和inout参数,但不能使用默认值,若要使用可变参数需要把它放在参数列表的最后。

先来看闭包表达式的写法:

reversed = sort(names, { (s1: String, s2: String) -> Bool in
return s1 > s2
})
完整的闭包表达式书写,参数部分和返回值部分和backwards函数一模一样,用关键字in引出闭包体,整个闭包用{ }括起来。

2.3类型推断闭包表达式方式

因为排序闭包作为参数传入函数,Swift可以推断闭包的参数类型和返回值类型。sort第一个参数类型决定了闭包的两个参数的类型,例子中是String类型;sort中第二个参数由names是StringArray推出它的类型是(String, String) -> Bool。因此闭包中的类型注释都可以省略,包括参数的括号和返回值的箭头。

reversed = sort(names, { s1, s2 in return s1 > s2 })
2.4省略return的闭包

单表达式的闭包可以隐式的省略return关键词,因为它只有一个语句,所以再次化简如下:

reversed = sort(names, { s1, s2 in s1 > s2 })
2.5参数名缩写的闭包

Swift为内联闭包提供参数名缩写的方式再一次简化闭包书写,用$0,$1,$2等等来指代参数名,如此一来可以省略参数列表以及in关键词,只剩下单表达式:

reversed = sort(names, { $0 > $1 })
2.6操作符函数

事实上操作符也可以看做一个闭包,由于String类型支持大于操作符>进行比较,所以我们能得到最终的最简形式:

reversed = sort(names, > )
3.后继闭包

后继闭包[Trailing Closure]支持将比较冗长的闭包体在函数调用时以后继的形式写在参数列表后面,不用必须写在括号内部。

还是以刚才的例子,把2.5中的函数调用的闭包以后继闭包的形式书写:

reversed = sort(names) { $0 > $1 }
4.捕获

闭包可以从它定义周围的上下文中捕获常量和变量,它能在闭包体内引用和修改这些常量和变量的值,即使定义这些常量和变量的原域已经不再存在。
嵌套函数是闭包的最简单的形式,它可以捕获它外围函数的参数和在外围函数中定义的任意常量和变量。
下面的例子中的incrementor是makeIncrement的嵌套函数,嵌套函数从上下文捕获两个值runnningTotal和amount。捕获了这些值之后,incrementor作为一个闭包被makeIncrementor返回,它的作用是每次在被调用时让runningTotal增加amount:
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
runningTotal是外围函数makeIncrement中定义的整形变量,但它可以在没有定义任何参数的嵌套函数incrementor中引用,这是因为incrementor把它们的值捕获并在其内部使用。
由于incrementor并没有修改amount的值,它在捕获amount之后存储了一个amount中存的值的拷贝,这个值一直和函数incrementor存在一起。
而runnningTotal变量的值每次在incrementor被调用的时候会被修改,因而incrementor是捕获的是runnningTotal变量的一个引用。捕获引用保证了每次在incrementor调用之后它的值不会被改变,而在下一次调用中继续其作用。

但是通过不同makeIncrement创建的函数是保存着各自的捕获值,包括存储的拷贝和引用,他们相互独立。例如:
let incrementBy10 = makeIncrementor(forIncrement : 10)

let incrementBy7 = makeIncrementor(forIncrement : 7)

println(incrementBy10())
println(incrementBy7())
println(incrementBy10())
println(incrementBy7())
//prints 10 7 20 14
5.闭包是引用类型

闭包是引用类型[Reference Type]意味着闭包在赋值是是赋予原闭包的引用,因此闭包捕获的拷贝值和引用值保持不变。
例如下面把incrementBy10赋值给另一个常量,runningTotal继续其作用:
let AnotherIncrementBy10 = incrementBy10
println(AnotherIncrementBy10())
//prints 30
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: