How to use Swift @autoclosure

后端 未结 6 1385
天命终不由人
天命终不由人 2020-12-02 04:44

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

@autoclosure() -> Bool

with an overloaded method

相关标签:
6条回答
  • 2020-12-02 04:49

    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()
    }
    
    0 讨论(0)
  • 2020-12-02 04:49

    @autoclosure is a function parameter which accept a cooked function(or returned type) meanwhile a general closure accept a raw function

    • @autoclosure argument type parameter must be '()'
      @autoclosure ()
      
    • @autoclosure accept any function with only appropriate returned type
    • Result of closure is calculated by demand

    Let's take a look at example

    func testClosures() {
    
        //closures
        XCTAssertEqual("fooWithClosure0 foo0", fooWithClosure0(p: foo0))
        XCTAssertEqual("fooWithClosure1 foo1 1", fooWithClosure1(p: foo1))
        XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: foo2))
    
        XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: { (i1, i2) -> String in
            return "fooWithClosure2 " + "foo2 " + String(i1 + i2)
        }))
    
        //@autoclosure
        XCTAssertEqual("fooWithAutoClosure HelloWorld", fooWithAutoClosure(a: "HelloWorld"))
    
        XCTAssertEqual("fooWithAutoClosure foo0", fooWithAutoClosure(a: foo0()))
        XCTAssertEqual("fooWithAutoClosure foo1 1", fooWithAutoClosure(a: foo1(i1: 1)))
        XCTAssertEqual("fooWithAutoClosure foo2 3", fooWithAutoClosure(a: foo2(i1: 1, i2: 2)))
    
    }
    
    //functions block
    func foo0() -> String {
        return "foo0"
    }
    
    func foo1(i1: Int) -> String {
        return "foo1 " + String(i1)
    }
    
    func foo2(i1: Int, i2: Int) -> String {
        return "foo2 " + String(i1 + i2)
    }
    
    //closures block
    func fooWithClosure0(p: () -> String) -> String {
        return "fooWithClosure0 " + p()
    }
    
    func fooWithClosure1(p: (Int) -> String) -> String {
        return "fooWithClosure1 " + p(1)
    }
    
    func fooWithClosure2(p: (Int, Int) -> String) -> String {
        return "fooWithClosure2 " + p(1, 2)
    }
    
    //@autoclosure
    func fooWithAutoClosure(a: @autoclosure () -> String) -> String {
        return "fooWithAutoClosure " + a()
    }
    
    0 讨论(0)
  • 2020-12-02 04:50

    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
    
    0 讨论(0)
  • 2020-12-02 04:59

    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.

    0 讨论(0)
  • 2020-12-02 05:02

    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.

    0 讨论(0)
  • 2020-12-02 05:06

    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.

    0 讨论(0)
提交回复
热议问题