How Do Closures Capture Values from Previous Calls

How do closures capture values from previous calls?

You've called makeCounter() once. That creates your new closure, and assigns it to counter1. This closure closes over the mutable var n, and will remain captured as long as this closure exists.

Calling counter1() will execute it, but it retains the same captured n, and mutates it. This particular "adder" will ALWAYS capture this same n, so long as it exists..

To get the behavior you're suggesting, you need to make new closures which capture new instances of n:

let counter1 = makeCounter()

counter1() // returns 1
counter1() // returns 2
counter1() // returns 3

var counter2 = makeCounter()
counter2() // returns 1
counter2 = makeCounter()
counter2() // returns 1
counter2 = makeCounter()
counter2() // returns 1

Now both counter1 and counter2 each have their own separate instances of n.

How closure captures values in Swift?

From Swift Programming Guide - Closures

A closure can capture constants and variables from the surrounding context in which it’s 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.

A closure captures variables, not the contents of variables. When we are talking about local variables in a function (which are normally allocated on stack), it makes sure they are accessible even when the function exits and other local variables are deallocated, therefore we can do things like this:

func myFunc() {
var array: [Int] = []

DispatchQueue.main.async {
// executed when myFunc has already returned!
array.append(10)
}
}

Your example is similar. The closure captures the variable. This is a variable on module level, therefore its scope always exists. When you reassign its value, it will affect the value read inside the closure.

Or, in other words, the closure will be equivalent to:

var closure = {
print(CurrentModule.element?.name ?? "default value")
}

where CurrentModule is the name of your main module (which is usually the name of your project).

To prevent this behavior and capture the value of the variable instead, we can use closure capture list. Unfortunately, the official documentation does not properly explain what exactly is a capture list. Basically, using a capture list you declare variables local to the closure using values that are available when the closure is created.

For example:

var closure = { [capturedElement = element] in
print(capturedElement?.name ?? "default value")
}

This will create a new variable capturedElement inside the closure, with the current value of variable element.

Of course, usually we just write:

var closure = { [element] in
print(element?.name ?? "default value")
}

which is a shorthand for [element = element].

How does the closure capture values?

You should look at the documentation, but here is an example sorting function (selection sort because that is the simplest):

func selectionSort<T>(inout array: [T], comparator: (T,T) -> Bool) {
var min: Int

for n in 0..<array.count {
min = n

for x in (n + 1)..<array.count {
if comparator(array[x], array[min]) {
min = x
}
}

if min != n {
let temp = array[min]
array[min] = array[n]
array[n] = temp
}
}
}

selectionSort(&names, backwards)

As you can see the function uses the closure to check for order, the exact number of times your closure gets called depends on the implementation of the sorted function's implementation.

How to capture the return value of a closure

If you wanted to do this purely with a closure (and not a function that accepts a closure), you could do something like:

let myClosure: (Int) -> String = { age in
if age < 10 {
return "You're just a baby"
}
else {
return "You can play the game"
}
}

let answer = myClosure(4) // "You're just a baby"

How does closures capture data?

You should first search for the difference between strong, weak, and unowned. There are PLENTY of answers here on SO about it.

Anyways, in this specific case:

Your closure has this code:

[unowned self,ac]

This is called a "capture list". It indicates the things that should be "captured" by value WHEN THE BLOCK is created. (if you don't specify them here, and you change the value somewhere after the block, the value inside the block would also be changed).

The reason why self is unowned and doesn't require to be deallocated is because unowned means:

"Don't worry about memory management for this variable, it will ALWAYS
have a value for the duration of my closure"

So, going back to unowned self, the reason you should declare weak or unowned the self variable from a closure is because if not you would create a retain cycle. Things can't be deallocated as long as something is referencing them. So in this case, your TableViewController is keeping your closure alive, and your closure is keeping your TableViewController alive. So because they are referencing each other none of them can get deallocated properly. -> Memory Leak

So we can conclude that self has to either be weak or unowned. For all intents and purposes in this example they are exactly the same. They both serve the purpose of "breaking the retain cycle" by removing the ability of the closure to keep self alive. So who will dealloc self you ask? your closure doesn't care. But think outside your closure. Your closure is being called by your TableViewController, so since there's no weird things going on here we can safely assume that, if there is an alert being shown it must definitively be shown over your TableViewController. So once you dismiss the alert or whatever, your TableViewController will keep working as usual. One you dismiss your TableViewController though, self WILL be deallocated (since the closure is referencing it as unowned), but by this point there is no way the alert is being shown. If you however do some weird things that make your TableViewController be dismissed WHILE the alert is still being shown, then once the user "submits" your app will crash. BECAUSE by declaring your variable unowned you basically made a promise to your closure that it wouldn't have to worry about the self entity, as it would always exist as long as your closure was alive.

How to capture a value in a Python Closure

Python closure is not weird if you know how closure internally works in python.

def makeListOfFuncs( strings):
list = []
for str in strings:
def echo():
return str
list.append( echo)
return list

You are returning a list of closures. variable "str" is shared between scopes, it is in two different scopes. When python sees it, it creates an intermediary object. this object contains a reference to another object which is "str". and for each closure, it is the same cell. You can test it:

closures_list= makeListOfFuncs( ['bird', 'fish', 'zebra'])
# Those will return same cell address
closures_list[0].__closure__
closures_list[1].__closure__
closures_list[2].__closure__

Sample Image

When you call makeListOfFuncs, it will run the for-loop and at the end of the loop, the intermediary object will point to "zebra". So when you call each closure print( animalFunc()) , it will visit the intermediary obj which will reference to "zebra".

You have to think about what happens when python creates a function and when it evaluates it.

def echo():
return str

We need to somehow able to capture the value of "str" as the function being created. Because if you wait until function gets evaluated, then it is too late. Another solution would be defining a default value:

def makeListOfFuncs( strings):
list = []
for str in strings:
def echo(y=str):
return y
list.append( echo)
return list

for animalFunc in makeListOfFuncs( ['bird', 'fish', 'zebra']):
print( animalFunc())

This solution works because default values get evaluated at creation time. So when echo is created, the default value of "str" will be used. the default value will not point to the cell.

in the first iteration, the default value will be "bird". Python will not see this as a free variable. in this case we are not even creating closure, we are creating function. in next iteration "fish" and in the last iteration "zebra" will be printed

Capturing Values in Closure


Does it mean that if a closure uses its surrounding context variable it does not get erased by the compiler and can cause memory leaks if not used correctly?

Absolutely, but that's not what is happening in this case. Let me break it down. Start inside the makeIncrementer function:

var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}

The func incrementer refers to runningTotal which is in scope from outside its own body. Therefore runningTotal is captured by func incrementer.

Now consider what the surrounding function makeIncrementer does:

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
// ...
func incrementer() -> Int {
// ...
}
return incrementer
}

It declares func incrementer and returns it. So now consider what happens when we call that surrounding function:

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
// ...
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)

That last line calls makeIncrementer, which we just said returns a function. It also assigns that function to a variable, incrementByTen. That is a local (automatic) variable. So yes, this function is being retained, but only so long as incrementByTen lives, which will not be long because it's just on the stack as an automatic local variable.

But as long as incrementByTen does live, it holds that function, and that function, we said at the start, has captured runningTotal and is maintaining it in a kind of floating local space. Therefore the function pointed to by incrementByTen can be called multiple times and that floating runningTotal keeps living and keeps being incremented.

Swift, how much of self do closures capture?


class B who is only referenced from class A.

I assume you mean something like this in A:

var objB: B?

Just before closure Z calls functions from class B, class B gets cleaned out.

Well, there is only one way to clean out B:

objB = nil

Now when you call B's methods in the closure, you will get an "unexpectedly found nil while unwrapping an optional value".

This is because you only captured self, which is A ,in the closure. You did not capture B at all. The instance of B is retained by the strong reference from A. Basically, B is not captured by the closure.

My confusion comes because in closure Z, i can specify [weak self], but I cannot do that for the functions I want to call in class B.

Well, you can capture B though. Just like this:

[b = self.objB] in ...

You can also make it weak:

[weak b = self.objB] in ...

This way you can also capture B in the closure.

Understanding closures - capturing values

This is as good a lesson about scope as it is capturing. In the original example, runningTotal is outside of the scope of incrementer() and incrementer() has captured this value. If you place runningTotal inside incrementer(), changing its scope, the output will reset to 0 every time it's called because runningTotal is now locally scoped and its value will not persist outside of the function—nothing is captured here.

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
func incrementer() -> Int {
var runningTotal = 0 // local
runningTotal += amount
return runningTotal
}
return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen() // 10
incrementByTen() // 10
incrementByTen() // 10

Assume runningTotal was a global variable, declared outside of everything. It should be clear now why the value isn't reset each time the function is called. But to incrementer(), this scope is no different than in the original example. In both cases, runningTotal is a variable outside of the scope of the function that it captured.

var runningTotal = 0 // global

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}

let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen() // 10
incrementByTen() // 20
incrementByTen() // 30


Related Topics



Leave a reply



Submit