closures in swift

ref – https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

Chapter 9: Closures


http://alisoftware.github.io/swift/closures/2016/07/25/closure-capture-1/

functions and closures are reference types

Closures are reference types. This means that when you assign a closure to more than one variable they will refer to the same closure. This is different from value type which make a copy when you assign them to another variable or constant.

Whenever you assign a function or a closure to a constant or a variable, you are actually setting that constant or variable to be a reference to the function or closure.

say incrementByTen is a function, we assign constant alsoIncrementByTen to it like so:

Closures can capture and store references to any constants or variables from the context in which they are defined.
In Swift, closures capture the variables they reference: variables declared outside of the closure but that you use inside the closure are retained by the closure by default, to ensure they are still alive when the closure is executed.

Declaring a closure

The general syntax for declaring closures is:

If the closure does not return any value you can omit the arrow (->) and the return type. This also applies to the case where the type of the closure can be infered.

Capturing Values

In Swift, closures capture the variables they reference: variables declared outside of the closure but that you use inside the closure are retained by the closure by default, to ensure they are still alive when the closure is executed.

In the below example, say we declare an object Pokemon. We delay 1 second, so that this function finishes running and exits. Then we see that closure of delay function will fun “closure()”.

That’s because the closure strongly captures the variable pokemon: as the Swift compiler sees that the closure references that pokemon variable inside the closure, it automatically captures it (strongly by default), so that this pokemon is alive as long as the closure itself is alive.

In this example, the closure itself gets released once it has been executed by GCD, so that’s when the pokemon’s deinit method gets called too.

IF Swift didn’t capture that pokemon variable automatically, that would mean that the pokemon variable would have had time to go out of scope when we reach the end of the demo1 function, and that pokemon would no longer exist when the closure would execute one second later… leading to a probable crash.

Captured variables evaluated on execution

If we have a closure that captures a pokemon variable, and that variable points to a first Pokemon instance, then a second Pokemon instance in the closure’s context, after the function exits, which pokemon instance will the closure reference?

result:

We print the new pokemon, not the old one! That’s because Swift captures variables by reference by default.

So here, we initialize pokemon to Pikachu, then we change its value to Mewtwo, so that Pikachu gets released — as no more variable retains it. Then one second later the closure gets executed and it prints the content of the variable pokemon that the closure captured by reference.

The closure didn’t capture “Pikachu” (the pokemon we got at the time the closure was created), but more a reference to the pokemon variable — that now evaluates to “Mewtwo” at the time the closure gets executed.

  1. Pickachu was created
  2. then the closure only captured a reference to the pokemon variable, not the actual Pickachu pokemon/value the variable contained.
  3. So when pokemon was assigned a new value “Mewtwo” later, Pikachu was not strongly referenced by anyone anymore and got released right away.
  4. But the pokemon variable (holding the “Mewtwo” pokemon at that time) was still strongly referenced by the closure
  5. So that’s the pokemon that was printed when the closure was executed one second later
  6. And that Mewtwo pokemon was only released once the closure was executed then released by GCD.
Capturing a variable to use in a closure as a constant copy

If you want to capture the value of a variable at the point of the closure creation, instead of having it evaluate only when the closure executes, you can use a capture list.

Capture lists are written between square brackets right after the closure’s opening bracket (and before the closure’s arguments / return type if any)3.

To capture the value of a variable at the point of the closure’s creation (instead of a reference to the variable itself), you can use the [localVar = varToCapture] capture list. Here’s what it looks like:

You can modify captured values in closures

Note that if the captured value is a var (and not a let), you can also modify the value from within the closure2.

This code prints the following:

Essentially, this is what’s happening:

Pikachu is referenced a constant pokemonCopy.

Here’s what happens:

  1. Pikachu is created
  2. then it is captured as a copy (capturing the value of the pokemon variable here) by the closure.
  3. So when a few lines below we assign pokemon to a new Pokemon “Mewtwo”, then “Pikachu” is not released just yet, as it’s still retained by the closure.
  4. When we exit the demo6 function’s scope, Mewtwo is released, as the pokemon variable itself — which was the only one strongly referencing it — is going out of scope.
  5. Then later, when the closure executes, it prints “Pikachu” because that was the Pokemon being captured at the closure creation’s time by the capture list.
  6. Then the closure is released by GCD, and so is the Pikachu pokemon which it was retaining.
Capture global context

In the beginning of the chapter I mentioned that closures can capture values. Let’s see what that means:

So a closure can remember the reference of a variable or constant from its context and use it when it’s called. In the example above the number variable is in the global context so it would have been destroyed only when the program would stop executing.

closure captures a variable that is not in the global context