Swift-@autoclosure 和 ??

前言

今天看喵神的Swift必备Tips,看到了一篇关于@autoclosure和??的文章,觉得这个挺好玩的,这里和大家一起分享一下。

@autoclosure

@autoclosure是很好玩的一个东东,它做的事情就是把一句表达式封装成一个闭包。这样的话有时候在语法上看起来会很美。
例如我们下面这样一个方法,执行结果为true的时候,进行下一步操作:

func autoclosureTest(_predicate:()->Bool){
    if _predicate(){
        print("执行结果为true的时候会打印")
    }
}

在调用的时候,我们需要这样写:

autoclosureTest(_predicate: {return 3>2})
或者
autoclosureTest(_predicate: {3>2})

因为这个闭包是最后一个参数,所以可以使用尾随闭包的方式进一步的简化:

autoclosureTest{3>2}

但是这样的话虽然书写方便了,但是表达上就不清楚了,那么咱们就请今天的嘉宾之一@autoclosure闪亮登场:

func autoclosureTest(_predicate:@autoclosure()->Bool){
    if _predicate(){
        print("执行结果为true的时候会打印")
    }
}

在参数名前面加上@autoclosure关键字,调用的时候就可以这样:

autoclosureTest(3>2)

这里swift将会把 3>2 这个表达式转换为 ()->Bool 。这样就会看起来简单明了。

??

??这个操作符很有用,它可以快速地对nil 进行条件判断。它可以判断输入并在当左侧的值不是nil的Optional值时,返回其value;当左侧是nil的时候返回右侧的值。比如:

var value:Int?
var startValue = 1
var currentValue = value  ?? startValue

这里我没有设置过value,因此最后startValue被赋值给了currentValue,我们点进去??的定义,我们会看到它有两个版本

public func ??(optional: T?, defaultValue: @autoclosure () throws -> T) rethrows -> T

public func ??(optional: T?, defaultValue: @autoclosure () throws -> T?) rethrows -> T?

这里我们的输入满足前者,虽然表面上看startValue只是一个Int,但是在使用它的时候,它被自动封装成了一个 ()->Int,那么我们来猜测一下??的实现吧:

func ??(optional: T?, defaultValue: @autoclosure () -> T) -> T {
            switch optional {
            case .Some(let value):
                return value
            case .None:
                return defaultValue()
            }
}

这里你可能会有疑惑,为什么要用autoclosure呢?直接接受 T作为参数返回不行吗?为什么用 ()->T 这样的形式再包装一遍呢?如果我们这里直接用T,那就是说在 ??操作符真正取值之前,我们就必须准备好一个默认值传入到这个方法中,一般情况下没有问题;但是要是这个默认值通过一系列的复杂计算得到的话,那么就可能会浪费---因为其实如果 optional 不是nil的话 我们实际上是完全没有用到这个默认值,而会直接返回 optional解包后的值。这样的开销是完全可以避免的,方法就是将默认值的计算推迟到 optional判定为nil 之后。

提示

最后要提示一下:@autoclosure 并不支持带有输入参数的写法,也就是说只有形如 ()->T 的参数才能使用这个特性进行简化。另外调用者往往很容易忽视@autoclosure这个特性,所以在写@autoclosure的方法时还需要特别小心,如果在容易产生歧义或者误解的时候,还是使用完整的闭包写法比较好。

参考

Swifter - Swift 必备 Tips (第三版)

你可能感兴趣的:(Swift-@autoclosure 和 ??)