在Objective-C中, 我们有block, 在Swift中, 我们有闭包, 两者之间基本上一致, 没有多大的区别, 非要说起不同, 也就是声明的语法有些差异, 还有一些特性上的差异, 下面让我们来看看.
1.前言
在讲闭包之前, 我们来看看一个简单的例子:
let array = [1, 2, 3, 5, 3, 10, 20, 30]
func backwards(s1: Int, s2: Int) -> Bool {
return s1 > s2
}
var reversed = array.sort(backwards)
print(reversed)
// 打印出来的结果:
// [30, 20, 10, 5, 3, 3, 2, 1]
这是一个排序的例子, 再想想, 貌似没有了更好的简洁方式了吧, 但这一切也只是在没有闭包之前, 下面让我们来看看闭包的强大之处.
2.闭包表达式语法
reversed = array.sort( { (s1: Int, s2: Int) -> Bool in return s1 > s2 } )
这个就是闭包表达式的语法, 完全不需要另外新建一个函数, 就可以完成排序的操作.
3.根据上下文推断类型
reversed = array.sort({s1, s2 in return s1 > s2})
这也是一种闭包的写法, Swift中有一个特性就是会根据上下文去推断类型, 而闭包也是可以如此的, 所以我们可以直接这么写.
4.单表达式闭包隐式返回
reversed = array.sort({ s1, s2 in s1 > s2})
在闭包里, 单行的闭包是可以不需要写return关键字的, 默认就会返回.
5.参数名称缩写
reversed = array.sort( { $0 > $1 } )
这种写法也是闭包的表达式, 不需要写参数名, 就可以直接排序.
6.运算符函数
reversed = array.sort(>)
这种写法也是闭包的表达式, 完全不需要任何参数, 直接写明一个运算符就可以完成排序, 但这种写法不太好, 只能完成一些比较简单的排序.
7.尾随闭包
所谓的尾随闭包, 就是自己声明一个闭包函数, 在别的地方去调用.
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 闭包函数体
}
// 写法一
someFunctionThatTakesAClosure({
// 闭包函数体
})
// 写法二
someFunctionThatTakesAClosure() {
// 闭包函数体
}
8.map函数
在Swfit原生当中, 有一个叫做map(_:)的函数, 它也是一个闭包类型, 下面让我们来看看.
let people = ["ZhangSan", "LiSi", "WangWu"]
let person = people.map { (var name) -> String in
print(name)
return name
}
map的用法和For-in有些类似, 它会映射数组的元素, 然后遍历, 直到遍历到最后一个元素为止.
9.自动闭包和闭包的延迟操作
var numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(numberArray.count)
// 输出的结果为: 9
let customerProvider = { numberArray.removeAtIndex(0) }
print(numberArray.count)
// 输出的结果为: 9
print("Now serving \(customerProvider())!")
// 输出的结果为: Now serving 1!
print(numberArray.count)
// 输出的结果为: 8
这里的customerProvider就相当于我们自定义的一个闭包函数, 虽然在闭包里, 已经移除了一个元素, 但该闭包并没有被调用, 所以元素不会被移除.
10.闭包的其他知识
这里还有一个叫做逃逸闭包, 和非逃逸闭包的概念:
所谓的逃逸闭包就是当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸, 也就是说, 在函数外部可访问, 而非逃逸闭包就是只能在函数内部访问, 一旦函数运行结束, 闭包也就结束了.
这里有几个关键字: @noescape, @autoclosure, @autoclosure(escaping)
@noescape关键字来标注的闭包, 就会变成非逃逸闭包, 说明该闭包不能被函数外部所访问.
@autoclosure关键字来标注的参数, 则会自动把参数转变成自动闭包, 注意@autoclosure关键也是含有@noescape关键的特性, 所以用@autoclosure来标注的闭包参数是不可以逃逸的.
@autoclosure(escaping)关键字来标注的参数, 则会自动把参数转变成自动闭包, 并且可逃逸.
var numberArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) {
closure()
}
func serveCunstomer(@autoclosure customerProvider: () -> Int) {
print("Now serving \(customerProvider())!")
}
var customerProviders: [() -> Int] = []
func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> Int) {
customerProviders.append(customerProvider)
}
这里有个例子, 有兴趣的朋友可以自己去研究看看, 如果想看更加详细的内容, 可以去参考官方文档.
这次就讲到这里, 下次继续