domo kun
domo kun

Reputation: 103

Regarding Swift's value capture on closures

I'm reading a book on swift and came across this example of closure value capturing.

func makeStateMachine(maxState: Int) -> StateMachineType { 
    var currentState: Int = 0

    return {
        currentState++
        if currentState > maxState {
            currentState = 0 
        }
        return currentState 
    }
} 

let bistate = makeStateMachine(1) 
println(bistate()); 
println(bistate()); 
println(bistate()); 
println(bistate());

The output should be '1 0 1 0 '

I understand how the return block captures the local value 'currentState' value after the function executes, but why isn't this value set back to 0 on the next function call? Is it because of the instance of the bistate constant? Or is it because currentState is initialized with a value 0 on the bistate initialization and the compiler infers that

var currentState: Int = 0

is ignored? I'm confused on how the above line is treated after the first call.

Upvotes: 4

Views: 1136

Answers (3)

GoZoner
GoZoner

Reputation: 70155

Nobody seems to have explained it...

The function makeStateMachine returns a closure having captured two variables: maxState and currentState. When invoked with a maxState of 1, the returned closure will increment currentState, compare it to 1 and then reset currentState to 0 if 1 is exceeded. Given multiple calls to the returned closure, the sequence of return values is 1, 0, 1, 0, .... More generally, for any maxState the sequence is 1, ..., maxState, 0, ..., maxState, ...

Upvotes: 1

ColinE
ColinE

Reputation: 70142

author of that book chapter here :-)

Yes, I can confirm that your comment is correct, the function returns a closure. I've added a few comments that hopefully clarify things:

func makeStateMachine(maxState: Int) -> StateMachineType { 
    // this variable is initialised when makeStateMachien is invoked
    var currentState: Int = 0

    // the function returns this closure
    return {
        // by using 'currentState' this variable is captured
        currentState++
        if currentState > maxState {
            currentState = 0 
        }
        return currentState 
    }
} 

When this function is invoked:

let bistate = makeStateMachine(1) 

The constant bistate now holds a reference to the closure that was returned by makeStateMachine.

Upvotes: 1

Antonio
Antonio

Reputation: 72760

This is explained in the first paragraph of Capturing Values:

A closure can capture constants and variables from the surrounding context in which it is defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.

and in a note a few paragraphs below that:

Swift determines what should be captured by reference and what should be copied by value.

So, the closure grabs a reference (and not a copy) of currentState. Any change to that variable inside the closure is done in the instance defined outside the closure, even if the scope no longer exists (because the makeStateMachine function has been executed).

Upvotes: 4

Related Questions