How to use Swift @autoclosure

2019-01-16 00:34发布

I noticed when writing an assert in Swift that the first value is typed as

@autoclosure() -> Bool

with an overloaded method to return a generic T value, to test existence via the LogicValue protocol.

However sticking strictly to the question at hand. It appears to want an @autoclosure that returns a Bool.

Writing an actual closure that takes no parameters and returns a Bool does not work, it wants me to call the closure to make it compile, like so:

assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__)

However simply passing a Bool works:

assert(false, "No user has been set", file: __FILE__, line: __LINE__)

So what is going on? What is @autoclosure?

Edit: @auto_closure was renamed @autoclosure

5条回答
唯我独甜
2楼-- · 2019-01-16 00:58

It's just a way to get rid of the curly braces in a closure call, simple example:

    let nonAutoClosure = { (arg1: () -> Bool) -> Void in }
    let non = nonAutoClosure( { 2 > 1} )

    let autoClosure = { (arg1: @autoclosure () -> Bool) -> Void in }
    var auto = autoClosure( 2 > 1 ) // notice curly braces omitted
查看更多
趁早两清
3楼-- · 2019-01-16 01:01

Consider a function that takes one argument, a simple closure that takes no argument:

func f(pred: () -> Bool) {
    if pred() {
        print("It's true")
    }
}

To call this function, we have to pass in a closure

f(pred: {2 > 1})
// "It's true"

If we omit the braces, we are passing in an expression and that's an error:

f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'

@autoclosure creates an automatic closure around the expression. So when the caller writes an expression like 2 > 1, it's automatically wrapped into a closure to become {2 > 1} before it is passed to f. So if we apply this to the function f:

func f(pred: @autoclosure () -> Bool) {
    if pred() {
        print("It's true")
    }
}

f(pred: 2 > 1)
// It's true

So it works with just an expression without the need to wrap it in a closure.

查看更多
何必那么认真
4楼-- · 2019-01-16 01:22

Here's a practical example — my print override (this is Swift 3):

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator:separator, terminator: terminator)
    #endif
}

When you say print(myExpensiveFunction()), my print override overshadows Swift's print and is called. myExpensiveFunction() is thus wrapped in a closure and not evaluated. If we're in Release mode, it will never be evaluated, because item() won't be called. Thus we have a version of print that doesn't evaluate its arguments in Release mode.

查看更多
做自己的国王
5楼-- · 2019-01-16 01:24

Description of auto_closure from the docs:

You can apply the auto_closure attribute to a function type that has a parameter type of () and that returns the type of an expression (see Type Attributes). An autoclosure function captures an implicit closure over the specified expression, instead of the expression itself. The following example uses the auto_closure attribute in defining a very simple assert function:

And here's the example apple uses along with it.

func simpleAssert(condition: @auto_closure () -> Bool, message: String) {
    if !condition() {
        println(message)
    }
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")

Basically what it means is you pass a boolean expression as that first argument instead of a closure and it automatically creates a closure out of it for you. That's why you can pass false into the method because it is a boolean expression, but can't pass a closure.

查看更多
叛逆
6楼-- · 2019-01-16 01:25

This shows a useful case of @autoclosure https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/

Now, the conditional expression passed as the first parameter to until will be automatically wrapped up into a closure expression and can be called each time around the loop

func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) {
    while !pred() {
        block()
    }
}

// doSomething until condition becomes true
until(condition) {
    doSomething()
}
查看更多
登录 后发表回答