Hi,SwiftGG 翻译组启用了新的域名:swiftgg.team今后翻译组的各项活动将会在新域名下开展,不要错过哦!

Swift 中有很多有用的属性关键字,在这里可以看到一些介绍, 不过这篇官方文档也只是大概的把常用的属性关键字做了一个简要的说明。有一些属性关键字,其实理解起来并不是那么容易。
这一期问答来谈谈@autoclosure属性。

@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带来的,而是本来闭包本身就带有这样的特性。以上的op1op2都是在调用的时候才去执行。

@noescape 和 @escape

对于autoclosure属性来讲,还有2个相关的属性不得不提。也就是@noescape@escape
这2个属性都是用来修饰闭包的。@noescape意思是非逃逸的闭包,而@escape则相反。
默认情况下,闭包是@escape的。表示此闭包还可以被其他闭包调用。比如我们常用的异步操作:

func executeAsyncOp(asyncClosure: () -> ()) -> Void {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
asyncClosure()
}
}

其中asyncClosuredispatch_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

  • 断言, 官方博客的介绍
    注意这篇文章很老了,Swift 1.2 中,@autoclosure的语法形式已经改变了。最新的@autoclosure语法是把@autoclosure放到了参数名的前面,之前是放到了参数和类型中间的。
  • ?? 操作符, 参见喵神的文章

同时在项目当中,一些比较耗时的操作,使用函数把操作封装起来,作为闭包传入到处理函数中。这时就可以用到@autoclosure, 简化调用的形式,比如以下一个例子:

/* 在文件读或写操作后,做一些其他操作 */
func doSomeOpAfterFileOp(@autoclosure fileOp: () -> Bool) {
if fileOp() == true {
//做其他操作
}
}

func fileOp() -> Bool {
return true
}

doSomeOpAfterFileOp(fileOp())

stackoverflow 相关问题整理

参考资料

本文相关代码地址

文章目录
  1. 1. @autoclosure
    1. 1.1. 用法
    2. 1.2. 作用
    3. 1.3. @noescape 和 @escape
    4. 1.4. 用法
    5. 1.5. stackoverflow 相关问题整理
    6. 1.6. 参考资料