微信号:swift-cafe

介绍:喝杯咖啡,聊聊技术,小而美的技术自媒体. 探讨 Swift;Objective-C;Cocoa 等开发技术.

Swift 中的 @autoclosure 关键字与闭包

2015-11-24 10:52 SwiftCafe

@autoclosure 关键字,是 Swift 引入的一个便捷操作,旨在让我们使用闭包作为参数的时候,更加的方便,简化语法形式,我们就来看看如何使用吧。

为何引入 @autoclosure 关键字

回答这个问题,首先我们来看一个例子:


func filter(predicate:() -> Bool) {

if predicate() {

//...

} else {

//...

}

}


这个函数接受一个参数 predicate, 它是一个闭包。 filter 函数会根据这个闭包,也就是 predicate 函数的返回值来进行相应的过滤操作。

那么,我们看看如何调用这个函数:


filter({
return 2 > 3
})


传入了一个闭包,这里返回 2 > 3 这个表达式的值。当然,我们还可以让这个闭包更加简化一些,闭包默认会把最后一个表示作为返回值,所以 return 关键字可以省略:


filter({
2 > 3
})


我们还可以再进行简化,由于我们的闭包只有一行代码,那么就可以写得更紧凑一些:


filter({ 2 > 3 })


已经很简单了,所有的逻辑都包含在一行代码中。

那么。。 还能不能再简化呢?

答案是。。 还可以。。

好吧。。。

我们可以在方法的参数定义中,使用 @autoclosure 关键字:


func filter(@autoclosure predicate:() -> Bool) {

if predicate() {

//...

} else {

//...

}

}


只是在 predicate 参数前面加上了 @autoclosure 声明。

这样做之后,我们的调用就可以简化成这样:


filter(2 > 3)


这样,连闭包两边的大括号都省略了,filter 方法自动将这个表达式包装成闭包,这就是 @autoclosure 的作用啦。

为何要用 @autoclosure

本着遇事都要多问一个为什么的心态,我想了想,为什么要用这个 @autoclosure 呢?如果只是为了简化参数传递,我们直接用值类型作为参数不就行了,这样不是更简单么,比如我们的 filter 函数完全可以这样定义:


func filter(predicate: Bool) {

if predicate {

//...

} else {

//...

}

}


这样,我们直接这样调用就可以了:


filter(2 > 3)


同样都是传入一个表达式作为参数,不是一样的效果么?

带着这个疑问,我搜索到了苹果官方 Swift 博客上面的一篇解释后,解开了我的疑惑。

@autoclosure 和普通表达式最大的区别就是,普通表达式在传入参数的时候,会马上被执行,然后将执行的结果作为参数传递给函数。

而使用 @autoclosure 标记的参数,虽然我们传入的也是一个表达式,但这个表达式不会马上被执行,而是要由调用的函数内来决定它具体执行的时间。

以我们这个 filter(2 > 3) 中的这个例子来看,如果我们使用的是普通参数,那么 2 > 3 这个表达式会先运算出结果,然后将结果传给 filter 函数。

而如果我们这个参数是 @autoclosure 标记的闭包,那么这个表达式就不会马上执行,而是交给 filter 函数决定什么时候执行这个表达式,或者,根本就不执行它。

这个区别在平常的情况下大家可能不会过多留意,但对于一些比较消耗性能的操作,这个执行机制就会有些影响了。

比如我们在 DEBUG 模式下的 asset 断言函数,它也会接受一个表达式:


func asset(condition: Bool) {

#if DEBUG

if !condition {
exit()
}

#endif

}


如果我们传入 asset 函数的表达式是一个比较消耗资源的操作,比如读取本地文件。


asset(loadLargeFile())


但我们的 asset 函数,实际上只会在 DEBUG 编译模式下才会检查这个表达式的值。而我们使用直接的参数值传递的话,loadLargeFile() 这个操作就会先于函数调用前就执行了,并没有达到我们预期的效果。

为了让表达式只在需要的时候才执行,我们可以引入了闭包的机制:


func assert(predicate : () -> Bool) {

#if DEBUG

if !predicate() {
exit()
}

#endif

}


这样,我们传入函数的闭包只有在 DEBUG 编译选项打开的时候才会被执行,其他情况下就不会白白的耗费性能了。

但使用闭包作为参数,又会带来调用上的不便,最简化的调用,也需要这样才行:


asset({ loadLargeFile() })


这样的语法看起来就很怪,所以 Swift 引入了 @autoclosure 关键字,我们把函数定义再修改一下:


func assert(@autoclosure predicate : () -> Bool) {

#if DEBUG

if !predicate() {
exit()
}

#endif

}


然后我们就可以直接将表达式作为闭包传入了:


asset(loadLargeFile())


虽然我们这次调用还是给 asset 参数传入一个表达式,但这个表达式是不会被立即执行的,而是根据 asset 的内部条件来判断是否执行。

经过这么一番探究,大家了解了吧,这才是 Swift 引入 @autoclosure 关键字的初衷。

Swift 的官方博客上有一篇文章对这个特性也进行了很详细的阐述:

https://developer.apple.com/swift/blog/?id=4

另外,咱们公众号里还有一篇关于闭包的文章,大家在公众号里面回复 [闭包] 关键字也可以继续了解。


 
SwiftCafe 更多文章 Swift 中的利刃,函数和闭包 浅谈 Swift 中的 Optionals Swift 中的枚举 extension 的一个应用 - 优化图片的读取机制 关于 Swift 2.0 - 语言新特性与革新
猜您喜欢 Linux内核测试套件LTP初探-服务器篇 高性能服务端系列 -- 处理器篇 玩转iOS9 必备教程!!—不学习会GO die Android微信上的Wear的开发总结 Airbnb干货分享:技术团队如何招聘和管理