Swift: Does Closure Have References to Constants or Variables

Swift: Does closure have references to constants or variables?

I think the confusion is by thinking too hard about value types vs reference types. This has very little to do with that. Let's make number be reference types:

class RefInt: CustomStringConvertible {
let value: Int
init(value: Int) { self.value = value }
var description: String { return "\(value)" }
}

let counter: () -> RefInt
var count = RefInt(value: 0)
do {
counter = {
count = RefInt(value: count.value + 1)
return count
}
}
count = RefInt(value: count.value + 1) // 1
counter() // 2
counter() // 3

Does this feel different in any way? I hope not. It's the same thing, just in references. This isn't a value/reference thing.

The point is that, as you note, the closure captures the variable. Not the value of the variable, or the value of the reference the variable points to, but the variable itself). So changes to the variable inside the closure are seen in all other places that have captured that variable (including the caller). This is discussed a bit more fully in Capturing Values.

A bit deeper if you're interested (now I'm getting into a bit of technicalities that may be beyond what you care about right now):

Closures actually have a reference to the variable, and changes they make immediately occur, including calling didSet, etc. This is not the same as inout parameters, which assign the value to their original context only when they return. You can see that this way:

let counter: () -> Int
var count = 0 {
didSet { print("set count") }
}

do {
counter = {
count += 1
print("incremented count")
return count
}
}

func increaseCount(count: inout Int) {
count += 1
print("increased Count")
}

print("1")
count += 1 // 1
print("2")
counter() // 2
print("3")
counter() // 3

increaseCount(count: &count)

This prints:

1
set count
2
set count
incremented count
3
set count
incremented count
increased Count
set count

Note how "set count" is always before "incremented count" but is after "increased count." This drives home that closures really are referring to the same variable (not value or reference; variable) that they captured, and why we call it "capturing" for closures, as opposed to "passing" to functions. (You can also "pass" to closures of course, in which case they behave exactly like functions on those parameters.)

How does closure store references to constants or values


Does it means It creates some sort of "Pointer" to a variable? If the
value changed the "de-referenced pointer" also changed to the new
value.

Yes.

I think Swift has no pointer concept.

It most certainly does, in the (implicit) form of reference types (classes), and the (explicit) form of UnsafePointer

Here's an example of a variable being captured in a closure. This all happens to be single threaded, but you could have this closure be dispatched by grand central dispatch. x is captured so that it exists for the entire life of the closure, even if the closure exists after x's declaring scope (f()) exits.

func f() {
var x = 0 //captured variable

_ = {
x = 5 //captures x from parent scope, and modifies it
}()

print(x) //prints 5, as x was modified by the closure
}

f()

I wrote another answer that explains the relationships between functions, closures, and other terms. It's worth reading, IMO.

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].

In Swift, what is the scope of local variable if it is used inside a closure?

This is why they're called closures.

Closures can capture and store references to any constants and
variables from the context in which they’re defined. This is known as
closing over those constants and variables. Swift handles all of the
memory management of capturing for you.

https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID103

Are Swift functions assigned/passed by value or by reference?

From the Apple documentation:

In the example above, incrementBySeven and incrementByTen are constants, but the closures these constants refer to are still able to increment the runningTotal variables that they have captured. This is because functions and closures are reference types.

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.

Swift any difference between Closures and First-Class Functions?

These notions are orthogonal. They are not directly related; they are two facts about functions in Swift.

  • Functions are first-class. This means they can be passed around — assigned as variables, passed into function parameters as arguments, and passed out of functions as results.

  • Functions are closures. This means that, at the point of definition, they capture the environment referred to inside the function body but declared outside the function body.

Here is an example (from a playground):

func multiplierMaker(i:Int) -> (Int) -> (Int) {
func multiplier(ii:Int) -> (Int) {
return ii*i
}
return multiplier
}
let g = multiplierMaker(10)
g(2) // 20

Think about the function multiplier:

  • The fact that multiplier can be returned as the result of the function multiplierMaker, and assigned to g, and that it has a well-defined type (Int) -> (Int), is because functions are first-class.

  • The fact that, when 10 is passed into multiplierMaker, the resulting multiplier function multiplies its parameter by 10, even when assigned to g and called later, is because functions are closures.

(Notice that this has nothing to do with anonymous functions. All answers or statements leading you to believe that closures have to do with anonymous functions are wrong. There are no anonymous functions in this example. An anonymous function is a closure, but only because all functions are closures.)

Why Swift global functions as a special case of closures capture global variables?

This is what "capturing" means:

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.

Global functions are said to not capture anything because you can never leave the global scope while your program is running, so there is no point in capturing anything. If it did capture something, that means global variables can still be changed even after you somehow "left" the global scope (maybe the program finishes running), which would be weird.

The global function in your code is not capturing anything. It can change the value of x simply because x is still in scope. You've not left the global scope.

Why this is not counted as a closure capture?

First, I would question your premise. A closure is a closure. All functions in Swift are closures, and they all capture in just the same way.

Second, I don't see what your code has to do with capturing or closures. You are not doing anything in your code that tests whether anything is being captured. The assignment of the type let fooA = addOne doesn't do anything interesting, and the code inside addOne doesn't do anything interesting either. You are merely adding two values at the time the code runs. Certainly the code inside addOne is permitted to refer to the global variable referenceInt, but that is merely because it is in scope. You aren't doing anything here that elicits the special powers of a closure.

Here's a modification of your code that does show capturing in action:

struct Test {
var referenceInt = 10

func addOne () -> Int {
return referenceInt + 1 // capture
}

mutating func test() {
let fooA = self.addOne
let fooB = self.addOne
let fooC = self.addOne

referenceInt = 100 // :)

print(fooA()) //prints 11
print(fooB()) //prints 11
print(fooC()) //prints 11

print(referenceInt) //prints 100
}

}

var t = Test()
t.test()

We change referenceInt before calling fooA and so on. Calling fooA still gives 11, because the value of self.referenceInt was captured before we changed referenceInt.



Related Topics



Leave a reply



Submit