Swift 的 autoclosure

Swift 的 autoclosure

在 Swift 中,如果一个函数,接收一个 closure 作为参数,我们可以这么写:

func op() -> Int {
    return 8
}

func function(a: () -> Int) {
    print(a())
}

//调用
function{ op() } //打印8

如果这个参数使用 autoclosure 修饰,则可以这么写:

func op() -> Int {
    return 8
}

func function(@autoclosure a: () -> Int) {
    print(a())
}

//调用
function(op()) //打印8

Swift 会将该参数自动包到 closure 里传给函数。那么除了语法区别,还有什么作用呢?
一个很好的例子就是 swift 自带的 ?? 操作符,这个操作符很简单,如果让我们自己实现,第一想到的就是这样写:

infix operator ??? {associativity right precedence 110}

func ???<T>(opt: T?, defaultVal: T) -> T {
    if let x = opt {
        return x
    } else {
        return defaultVal
    }
}

let opt: Int? = 1
let nilopt: Int? = nil
let a: Int = opt ??? 10
let b: Int = nilopt ??? 10
print(a)  //打印1
print(b)  //打印10

看起来不错,但是其实有个问题,假如我们的 defaultVal 传的是一个返回 Int 的函数,那么即使 opt 不是 nil,defaultVal 值不会被用到,这个函数也会被执行:

func op() -> Int {
    print("run")
    return 8
}

let opt: Int? = 1
let a: Int = opt ??? op()
print(a)
//打印 run\n 1

这里 op 函数的执行是没有必要的,因为 opt 是有值的,op 函数的结果不会被用到,这里做了一次额外不用的运算,如果 op 函数的性能开销很大,对整个 app 的性能是有影响的。

解决方法就是把 defaultVal 作为一个 closure,这样只有在 opt 为 nil 时,才会去执行 closure 里的代码

func op() -> Int {
    print("run")
    return 8
}

infix operator ??? {associativity right precedence 110}

func ???<T>(opt: T?, defaultVal: () -> T) -> T {
    if let x = opt {
        return x
    } else {
        return defaultVal()
    }
}

let opt: Int? = 1
let a: Int = opt ??? {op()}
print(a)
//打印 1

看到那奇怪的中括号了么,因为我们 defaultVal 参数接受的是一个 closure,参数打上大括号作为一个 closure 传入。
这显然是不太能接受的语法。所以我们需要 autoclosure,把参数自动包成 closure。

func op() -> Int {
    print("run")
    return 8
}

infix operator ??? {associativity right precedence 110}

func ???<T>(opt: T?, @autoclosure defaultVal: () -> T) -> T {
    if let x = opt {
        return x
    } else {
        return defaultVal()
    }
}

let opt: Int? = 1
let a: Int = opt ??? op()
print(a)
//打印 1
2016-02-24 16:10572
  • __z2xy__2016-02-25 00:19

    写的不错.

    有点类似http://swifter.tips/autoclosure/这里的.

    不过也可以看到apple官方自己实现的

    https://github.com/apple/swift/blob/67fda25715cf1de830b78c11b30700fdeaa5f251/stdlib/public/core/Optional.swift#L244-L254

    @_transparent
    @warn_unused_result
    public func ??  (optional: T?, @autoclosure defaultValue: () throws -> T)
        rethrows -> T {
      switch optional {
      case .Some(let value):
        return value
      case .None:
        return try defaultValue()
      }
    }
    
    @_transparent
    @warn_unused_result
    public func ??  (optional: T?, @autoclosure defaultValue: () throws -> T?)
        rethrows -> T? {
      switch optional {
      case .Some(let value):
        return value
      case .None:
        return try defaultValue()
      }
    }
  • 极分享2016-03-01 17:18

    果然是大神啊