Swift 中有很多有用的属性关键字,在这里可以看到一些 介绍 , 不过这篇官方文档也只是大概的把常用的属性关键字做了一个简要的说明。有一些属性关键字,其实理解起来并不是那么容易。
这一期问答来谈谈 @autoclosure
属性。
此属性关键字用在函数或者方法的闭包参数前面,但是闭包类型被限定在无参闭包上: () -> T
。例如以下的一个例子:
func doSomeOperation(@autoclosure op: () -> Bool) {
op()
}
// 调用如下:
doSomeOperation(2 > 3)
看了以上代码,大家估计会比较困惑,这样定义了去掉 @autoclosure
有什么区别?我们先直接抛出 @autoclosure
的全部作用,然后再来分析,有和没有的区别:
@autoclosure
关键字能简化闭包调用形式 @autoclosure
关键字能延迟闭包的执行 关于第一点,大家可能看上面的例子代码,调用带 @autoclosure
的函数形式很自然,其实如果去掉 @autoclosure
,我们就不能这样调用了:
func doSomeOperationWithoutAutoclosure(op: () -> Bool) {
op()
}
doSomeOperationWithoutAutoclosure({2 > 3})
doSomeOperationWithoutAutoclosure{2 > 3} //尾闭包的简化
很容易看的出来,使用括号来写起来更加自然。其实如果是尾闭包的形式,也可以接受。只是尾闭包只能是放到参数列表的最后才能这样使用。而 @autoclosure
是可以修饰任何位置的参数:
func doSomeOperationWithTwoAutoclosure(@autoclosure op1: () -> Bool, @autoclosure op2: () -> Bool) {
op1()
op2()
}
doSomeOperationWithTwoAutoclosure(2 > 3, op2: 3 > 2)
@autoclosure
本身取名也有体现出这种语法的意思。直译为自动闭包,也就是会把 (2 > 3)
这样的语法自动转换为闭包执行。
我们再来看看延迟执行这事。其实延迟这个特性,本身不是 @autoclosure
带来的,而是本来闭包本身就带有这样的特性。以上的 op1
和 op2
都是在调用的时候才去执行。
对于 autoclosure
属性来讲,还有2个相关的属性不得不提。也就是 @noescape
和 @escape
。
这2个属性都是用来修饰闭包的。 @noescape
意思是非逃逸的闭包,而 @escape
则相反。
默认情况下,闭包是 @escape
的。表示此闭包还可以被其他闭包调用。比如我们常用的异步操作:
func executeAsyncOp(asyncClosure: () -> ()) -> Void {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
asyncClosure()
}
}
其中 asyncClosure
在 dispatch_async
中的闭包中调用,完成异步的操作。因为闭包默认是 @escape
的,以上代码是可以运行的。但是当我们在 asyncClosure
前面加入 @noescape
属性时候,编译器就会报错:
closure use of @noesape parameter `asyncClosure` may allow it to escape
@noescape
属性是在 Swift 1.2 中引入的,把传入闭包参数的调用限制在调用的函数体内,对性能有一定的提升,同时将闭包标注为 @noescape
使你能在闭包中隐式地引用self。
在 Swift 标准库中很多方法,都用了 @noescape
属性,比如 Array 对应的方法 map,filter 和 reduce:
func map<T>(@noescape transform: (Self.Generator.Element) -> T) -> [T]
func filter(@noescape includeElement: (Self.Generator.Element) -> Bool) -> [Self.Generator.Element]
func reduce<T>(initial: T, @noescape combine: (T, Self.Generator.Element) -> T) -> T
而 @autoclosure
默认是 @noescape
的,要使用逃逸特性,请使用 @autoclosure(escaping)
官方库中在这些地方用到了 @autoclosure
:
@autoclosure
的语法形式已经改变了。最新的 @autoclosure
语法是把 @autoclosure
放到了参数名的前面,之前是放到了参数和类型中间的。 同时在项目当中,一些比较耗时的操作,使用函数把操作封装起来,作为闭包传入到处理函数中。这时就可以用到 @autoclosure
, 简化调用的形式,比如以下一个例子:
/* 在文件读或写操作后,做一些其他操作 */
func doSomeOpAfterFileOp(@autoclosure fileOp: () -> Bool) {
if fileOp() == true {
//做其他操作
}
}
func fileOp() -> Bool {
return true
}
doSomeOpAfterFileOp(fileOp())
@autoclosure
@noescape
的理解。 @autoclosure
@autoclosure
本文相关代码地址