@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 的官方博客上有一篇文章对这个特性也进行了很详细的阐述:
developer.apple.com/swift/blog/…
另外,咱们公众号里还有一篇关于闭包的文章,大家在公众号里面回复 [闭包] 关键字也可以继续了解。
本站文章均为原创内容,如需转载请注明出处,谢谢。如果还想看更多愉悦文章,还可来微信公众号 swift-cafe 发现更多。