allenlinli
allenlinli

Reputation: 2154

Why autoclosure delay evaluation and normal closure does not?

Since Apple states An autoclosure lets you delay evaluation, because the code inside isn’t run until you call the closure., why autoclosure delay evaluation and normal course does not?

I borrow the snippet with autoclosure from John Sundell to compare with/without autoclosure.

func assert2(_ expression: @autoclosure () -> Bool,
            _ message: @autoclosure () -> String) {
    guard isDebug else {
        return
    }

    // Inside assert we can refer to expression as a normal closure
    if !expression() {
        assertionFailure(message())
    }  
}
func assert3(_ expression: () -> Bool,
            _ message: () -> String) {
    guard isDebug else {
        return
    }

    // Inside assert we can refer to expression as a normal closure
    if !expression() {
        assertionFailure(message())
    }  
}

But it seems the message() will not be executed in both cases.

The only difference for me is I need to make closure manually:

override func viewDidLoad() {
        super.viewDidLoad()

        assert2(false, "hello2")
        assert3({return false}, {return "hello3"})
}

Is there some other reason that Apple and John Sundell say the autoclosure delay the execution? For example, are normal closure being pre-evaluated because of optimization from Xcode? Or any other reasons that closures behave in this way?

Please provide an official document if there's any which explaining this point explicitly.

Upvotes: 0

Views: 112

Answers (1)

Sweeper
Sweeper

Reputation: 274480

I think you misunderstood the distinction that the documentation is trying to draw. When the documentation says:

An autoclosure lets you delay evaluation, because the code inside isn’t run until you call the closure.

It's not comparing @autoclosure () -> Bool (an "autoclosure") against () -> Bool (a "normal closure"). It's comparing @autoclosure () -> Bool to Bool.

The documentation is assuming that the caller stays the same, specifically in the case that the caller passes some expressions to the method. For example, for such a calling code:

assert(someBoolFunction(), someStringFunction())

Using @autoclosure will allow someBookFunction to be run at a later time (or not at all), whereas accepting a Bool will cause someBoolFunction to be called immediately, even before assert is called. This is because @autoclosure indicates that whatever expression is passed is wrapped into a closure via the magics of syntactic sugar.

Note that changing a function's parameter from Bool to @autoclosure () -> Bool is generally not a breaking change to the caller (the caller will still be able to pass expressions to the function), which is why this is a meaningful comparison.

Upvotes: 1

Related Questions