There seems to be a curious syntax glitch in the capture list syntax in Swift. If I declare multiple captured variables, the capture specifier only applies to the first one:
let closure = { [unowned x, y] in … }
Now I would expect y to be unowned, but it doesn’t seem to be the case:
class Test {
var callback: (Void -> Void)?
init() {
print("init")
}
deinit {
print("deinit")
}
}
func makeScope() {
let x = Test()
let y = Test()
y.callback = { [unowned x, y] in
print(y)
}
}
makeScope()
print("done")
This prints:
init
init
deinit
done
So y seems to be captured strongly and creates a retain cycle, preventing the object from being deallocated. Is that so? If yes, does it make sense to permit an “empty” capture specifier in the list? Or is there a reason [unowned x, y] is not treated as [unowned x, unowned y]?
... does it make sense to permit an “empty” capture specifier in the list?
Yes it does.
The capture specifiers ("weak", "unowned" and its variations) can only be used with reference types, but there are also cases where you want to capture a value type
(here is one example:
Pass value to closure?).
You also may want to capture a reference type strongly.
Capturing a reference type ensures that the reference (pointer)
itself is captured by value, as demonstrated in the following example:
class MyClass {
let value : String
init(value : String) {
self.value = value
}
}
var ref = MyClass(value: "A")
let clo1: () -> Void = { print(ref.value) }
let clo2: () -> Void = { [ref] in print(ref.value) }
ref = MyClass(value: "B")
clo1() // Output: B
clo2() // Output: A
When the first closure is executed, ref inside the closure
is a reference to the object created as MyClass(value: "B").
The second closure captures the value of ref at the time the
closure is created, and this does not change when a new value
is assigned to var ref.
According to the syntax EBNF, this treatment of unowned capture specifier is fully intentional:
closure-signature → parameter-clause function-resultopt in
closure-signature → identifier-list function-resultopt in
closure-signature → capture-list parameter-clause function-resultopt in
closure-signature → capture-list identifier-list function-resultopt in
closure-signature → capture-list in
capture-list → [capture-list-items]
capture-list-items → capture-list-item capture-list-item , capture-list-items
capture-list-item → capture-specifieropt expression
capture-specifier → weak | unowned | unowned(safe) | unowned(unsafe)
The three lines at the bottom defining <capture-list-items>, <capture-list-item>, and <capture-specifier> productions are most relevant here.
The <capture-list-items> production is a comma-separated list of <capture-list-item>, with the capture-specifier is attached to each individual <capture-list-item>, not to <capture-list-items> list as a whole.
This makes perfect sense, because it gives programmers full control over capturing of individual arguments. The alternative when the specifier would apply to the whole list would take away this flexibility.
why would one want to include an identifier in the capture list without modifying its capture specifier?
It appears that the philosophy of Swift's designers is to provide smart default behaviors whenever it is possible. In most cases, Swift can figure out a way to capture an expression that makes most sense based on the type of the expression without any involvement from the programmer. Explicit capture specifier is left for exceptional situations, when the compiler does not have enough information to figure out the proper way of capturing a variable based on the context.
To answer your specific questions:
Why is the capture specifier optional in capture lists?
Because the default behavior is to capture any necessary variables (reference types are captured strongly). By default, you don't need to specify them explicitly in the capture list if you want to use their values. (Although qualifying with self.property will be necessary if you are capturing self.)
…is there a reason [unowned x, y] is not treated as [unowned x, unowned y]?
For the same reason: the default is to capture strongly. The unowned doesn't apply to other items in the capture list; that's just not how the syntax works right now.
Related
If i run the following code in XCode 12 playground (Swift 5.3) I get the same result from two listings:
import Foundation
var dict = NSMutableDictionary()
dict["x"] = 42
func stuff(_ d: inout NSMutableDictionary) {
d["x"] = 75
}
stuff(&dict)
dump(dict) // x is 75
the other:
import Foundation
var dict = NSMutableDictionary()
dict["x"] = 42
func stuff(_ d: NSMutableDictionary) {
d["x"] = 75
}
stuff(dict)
dump(dict) // x is 75 still
As per the documentation here, the second listing should give me an error:
https://docs.swift.org/swift-book/LanguageGuide/Functions.html
But it works anyway.
Is this because the enforcement of these in-out rules is constrained to Swift only types, and Cocoa types are exempt?
This works not because Cocoa types are exempt, but because NSMutableDictionary is a class (as opposed to a struct), and the inout does not refer to what you might be thinking.
Unfortunately, the documentation you link to (and the more in-depth documentation on inout parameters it links to) doesn't make it clear what "value" really means:
An in-out parameter has a value that is passed in to the function, is modified by the function, and is passed back out of the function to replace the original value
The following statement hints at it a little, but could be clearer:
You can only pass a variable as the argument for an in-out parameter. You cannot pass a constant or a literal value as the argument, because constants and literals cannot be modified.
The "value" the documentation describes is the variable being passed as inout. For value types (structs), this is meaningful because every variable holding a value of those types effectively holds a copy of that value.
var a = MyGreatStruct(...)
var b = a
// a and b are not directly linked in any way
Passing a struct to a function normally copies the value into a new local variable (new variable = copy), whereas you can imagine inout giving you direct access to the original variable (no new variable).
What's not described is that the effect is identical for classes, which behave differently.
let a = MyGreatClass(...)
let b = a
// modifying `a` will modify `b` too since both point to the same instance
Passing a class to a function also copies the variable into a new local variable, but the copy isn't meaningful — both variables hold the same thing: a reference to the object itself in memory. Copying in that sense doesn't do anything special, and you can modify the object from inside of the function the same way you could from outside. inout for classes behaves the same way as for structs: it passes the original variable in by reference. This has no bearing on the majority of the operations you'd want to perform on the object anyway (though it does allow you to make the variable point to a different object from within the function):
var a = MyGreatClass("Foo")
// func foo(_ value: MyGreatClass) {
// value = MyGreatClass("Bar") // <- not allowed since `value` isn't mutable
// }
func foo(_ value: inout MyGreatClass) {
value = MyGreatClass("Bar")
}
print(ObjectIdentifier(a)) // <some pointer>
foo(&a)
print(ObjectIdentifier(a)) // <some other pointer>
Suppose I have a Swift class like this:
#objc final MyClass : NSObject
{
let classPropertyString = "A class property"
func doStuff()
{
let localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async { [classPropertyString] in
// Do things with 'classPropertyString' and 'localString'
}
}
}
My question is: when I write a capture list, am I responsible for EXHAUSTIVELY listing all the things to which I want the closure to hold a strong reference?
In other words, if I omit localString from my capture list (as I've done here), will the closure still automatically capture a strong reference to it or am I in for a bad time?
There are several minor quirks with your question that make it tricky to answer clearly, but I think I understand the underlying concern, and the short answer is "no." But your example is impossible, so the answer is "it's impossible." And if it were possible, there'd be no strong reference (nor would there be a need for one), so the question still would be kind of "it's impossible." Even so, let's walk through what's going on here.
First, closure can't reference localString unless it's reassigned somehow in the comment inside doStuff(). closure is assigned at a level where localString is not in scope. Closures can only capture variables that are in scope when they are assigned, not when they're called. But let's go back to the original version of this question, before it was edited. That version did have the case you're describing:
#objc final myClass : NSObject
{
let classPropertyString = "A class property"
func doStuff()
{
let localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async { [classPropertyString] in // (1)
// Do things with 'classPropertyString' and 'localString'
}
// (2)
}
}
There's no problems here. classPropertyString is copied into the closure, avoiding any retain loops. localString is referenced by the closure, and so it's preserved as long as the closure exists.
Because you listed classPropertyString in the capture list, it is evaluated at point (1) and copied into the closure. Because you implicitly captured localString, it is treated as a reference. See Capture Lists in the Swift Programming Language Reference for some excellent examples of exactly how this works in different cases.
In no case (*) will Swift allow the underlying storage for something you're using in a closure to disappear behind your back. That's why the typical concern is excessive retains (memory leaks) rather than dangling references (crashes).
(*) "In no case" here is a lie. There are several ways that Swift will allow it, but almost all of them involve "Unsafe" which is your warning about that. The major exception is unowned, and of course anything involving ! types. And Swift is not typically thread-safe, so you need to be careful about that...
The last comment about thread-safety is a place where the subtle distinctions between implicit and explicit captures can really matter. Consider this case where you modify an implicitly captured value on two queues:
func doStuff() -> String
{
var localString = "An object local to this function"
DispatchQueue.global(qos: .userInitiated).async {
localString = "something else"
callFunction(localString)
}
localString = "even more changes"
return localString
}
What happens in that case? Good grief, never do that. I believe it's undefined behavior and that localString could be anything including corrupted memory, at least in the most general case (it might be defined behavior for calling .async; I'm not sure). But don't do it.
But for your normal cases, there is no reason to explicitly capture local variables. (I sometimes wish Swift had gone the C++ way and said it was required, but it isn't.)
Ok, one more way implicit and explicit are different that might drive home how they work. Consider a stateful closure like this (I build these pretty often):
func incrementor() -> () -> Int {
var n = 0
return {
n += 1
return n
}
}
let inc = incrementor()
inc() // 1
inc() // 2
inc() // 3
let inc2 = incrementor()
inc2() // 1
See how the local variable n is captured by the closure, and can be modified after it goes out of scope. And see how inc2 has its own version of that local variable. Now try that with explicit capture.
func incrementor() -> () -> Int {
var n = 0
return { [n] in // <---- add [n]
n += 1 // Left side of mutating operator isn't mutable: 'n' is an immutable capture
return n
}
}
Explicit captures are copies and they're immutable. Implicit captures are references, and so have the same mutability as the thing they reference.
I am going through the following tutorial on RxSwift:
http://adamborek.com/thinking-rxswift/
and having trouble understanding the following pattern:
searchBar.rx.text.orEmpty
------------> .flatMap { [spotifyClient] query in
return spotifyClient.rx.search(query: query)
}.map { tracks in
return tracks.map(TrackRenderable.init)
}
This square brackets input parameter: [spotifyClient] query seems very weird for me. I looked over official Apple documentation for closures and functions and I can not see any examples of such input parameters. In Objective C this would not bother me much, but it is Swift. Could anyone explain, what this parameter means here?
You will need to understand the variable capturing of closure idea.
Consider this example:
struct Calculator {
var a: Int
var b: Int
var sum: Int {
return a + b
}
}
Then you use this as:
let calculator = Calculator(a: 3, b: 5)
// You define a closure where you will use this calculator instance
let closure = {
// closure captures the variables that are declared prior to the declaration of the closure.
// your calculator instance is being captured here
// it's default variable capture
print("The result is \(calculator.sum)")
}
closure() // Prints "The result is 8"
Till now, everything is okay. You get what's expected.
Now consider you declare the calculator instance as var because in some point you need to mutate it's state. This is the case where complexity arises. Look:
var calculator = Calculator(a: 3, b: 5)
let closure = {
print("The result is \(calculator.sum)")
}
// You change the state of your calculator instance anytime before the closure gets executed
calculator.b = 20
// When the closure actually executes, you will be affected by any changes outside the closure
closure() // Prints "The result is 23"
So, the default variable capture isn't really helping you, instead it's creating problem in your case.
If you want to prevent this behaviour and print 8 even if the properties change after their capturing inside the closure, we can explicitly capture the variable with a capture list like this:
// [calculator] is your capture list
let closure = { [calculator] in
print("The result is \(calculator.sum)")
}
// change anything with calculator instance
calculator.b = 20
// execute the closure
closure() // Prints "The result is 8"
Capture List keeps immutable copy of the variable(s). Thanks to this copy, further changes to calculator, outside the closure, will not affect the closure.
You can capture multiple variables at once, hence it's called Capture List. Example:
let closure = { [variable1, variable2, variable3] in
print(variable1)
print(variable2)
print(variable3)
}
I recommend you read this article Capturing Values In Swift Closures.
Now, in your case spotifyClient is an instance of a class that may be responsible to make API calls. This instance may need some changes for calling different APIs. So, to prevent the affect of any changes to spotifyClient outside this closure you capture this instance in a Capture List.
Capture List vs. Parameter List:
You are confusing the parameter list with the capture list. The generic syntax is:
{ [capture list] (parameter list) in
...
...
}
Now take a look at the modified version of the above example:
let closure: (String)-> Void = { [calculator] stringParameter in // When using single parameter, you can always omit the () parentheses
print("\(stringParameter). The result is \(calculator.sum)")
}
// change anything with calculator instance
calculator.b = 20
// execute the closure
closure("Hey") // Prints "Hey. The result is 8"
After reading:
https://github.com/rodionovd/SWRoute/wiki/Function-hooking-in-Swift
https://github.com/rodionovd/SWRoute/blob/master/SWRoute/rd_get_func_impl.c
I understood that Swift function pointer is wrapped by swift_func_wrapper and swift_func_object (according to the article in 2014).
I guess this still works in Swift 3, but I couldn't find which file in https://github.com/apple/swift best describes these structs.
Can anyone help me?
I believe these details are mainly part of the implementation of Swift's IRGen – I don't think you'll find any friendly structs in the source showing you the full structure of various Swift function values. Therefore if you want to do some digging into this, I would recommend examining the IR emitted by the compiler.
You can do this by running the command:
xcrun swiftc -emit-ir main.swift | xcrun swift-demangle > main.irgen
which will emit the IR (with demangled symbols) for a -Onone build. You can find the documentation for LLVM IR here.
The following is some interesting stuff that I've been able to learn from going through the IR myself in a Swift 3.1 build. Note that this is all subject to change in future Swift versions (at least until Swift is ABI stable). It goes without saying that the code examples given below are only for demonstration purposes; and shouldn't ever be used in actual production code.
Thick function values
At a very basic level, function values in Swift are simple things – they're defined in the IR as:
%swift.function = type { i8*, %swift.refcounted* }
which is the raw function pointer i8*, along with a pointer to its context %swift.refcounted*, where %swift.refcounted is defined as:
%swift.refcounted = type { %swift.type*, i32, i32 }
which is the structure of a simple reference-counted object, containing a pointer to the object's metadata, along with two 32 bit values.
These two 32 bit values are used for the reference count of the object. Together , they can either represent (as of Swift 4):
The strong and unowned reference count of the object + some flags, including whether the object uses native Swift reference counting (as opposed to Obj-C reference counting), and whether the object has a side table.
or
A pointer to a side table, which contains the above, plus the weak reference count of the object (on forming a weak reference to an object, if it doesn't already have a side table, one will be created).
For further reading on the internals of Swift reference counting, Mike Ash has a great blog post on the subject.
The context of a function usually adds extra values onto the end of this %swift.refcounted structure. These values are dynamic things that the function needs upon being called (such as any values that it has captured, or any parameters that it has been partially applied with). In quite a few cases, function values won't need a context, so the pointer to the context will simply be nil.
When the function comes to be called, Swift will simply pass in the context as the last parameter. If the function doesn't have a context parameter, the calling convention appears to allow it to be safely passed anyway.
The storing of the function pointer along with the context pointer is called a thick function value, and is how Swift usually stores function values of known type (as opposed to a thin function value which is just the function pointer).
So, this explains why MemoryLayout<(Int) -> Int>.size returns 16 bytes – because it's made up of two pointers (each being a word in length, i.e 8 bytes on a 64 bit platform).
When thick function values are passed into function parameters (where those parameters are of non-generic type), Swift appears to pass the raw function pointer and context as separate parameters.
Capturing values
When a closure captures a value, this value will be put into a heap-allocated box (although the value itself can get stack-promoted in the case of a non-escaping closure – see later section). This box will be available to the function through the context object (the relevant IR).
For a closure that just captures a single value, Swift just makes the box itself the context of the function (no need for extra indirection). So you'll have a function value which looks like a ThickFunction<Box<T>> from the following structures:
// The structure of a %swift.function.
struct ThickFunction<Context> {
// the raw function pointer
var ptr: UnsafeRawPointer
// the context of the function value – can be nil to indicate
// that the function has no context.
var context: UnsafePointer<Context>?
}
// The structure of a %swift.refcounted.
struct RefCounted {
// pointer to the metadata of the object
var type: UnsafeRawPointer
// the reference counting bits.
var refCountingA: UInt32
var refCountingB: UInt32
}
// The structure of a %swift.refcounted, with a value tacked onto the end.
// This is what captured values get wrapped in (on the heap).
struct Box<T> {
var ref: RefCounted
var value: T
}
In fact, we can actually verify this for ourselves by running the following:
// this wrapper is necessary so that the function doesn't get put through a reabstraction
// thunk when getting typed as a generic type T (such as with .initialize(to:))
struct VoidVoidFunction {
var f: () -> Void
}
func makeClosure() -> () -> Void {
var i = 5
return { i += 2 }
}
let f = VoidVoidFunction(f: makeClosure())
let ptr = UnsafeMutablePointer<VoidVoidFunction>.allocate(capacity: 1)
ptr.initialize(to: f)
let ctx = ptr.withMemoryRebound(to: ThickFunction<Box<Int>>.self, capacity: 1) {
$0.pointee.context! // force unwrap as we know the function has a context object.
}
print(ctx.pointee)
// Box<Int>(ref:
// RefCounted(type: 0x00000001002b86d0, refCountingA: 2, refCountingB: 2),
// value: 5
// )
f.f() // call the closure – increment the captured value.
print(ctx.pointee)
// Box<Int>(ref:
// RefCounted(type: 0x00000001002b86d0, refCountingA: 2, refCountingB: 2),
// value: 7
// )
ptr.deinitialize()
ptr.deallocate(capacity: 1)
We can see that by calling the function between printing out the value of the context object, we can observe the changing in value of the captured variable i.
For multiple captured values, we need extra indirection, as the boxes cannot be stored directly as the given function's context, and may be captured by other closures. This is done by adding pointers to the boxes to the end of a %swift.refcounted.
For example:
struct TwoCaptureContext<T, U> {
// reference counting header
var ref: RefCounted
// pointers to boxes with captured values...
var first: UnsafePointer<Box<T>>
var second: UnsafePointer<Box<U>>
}
func makeClosure() -> () -> Void {
var i = 5
var j = "foo"
return { i += 2; j += "b" }
}
let f = VoidVoidFunction(f: makeClosure())
let ptr = UnsafeMutablePointer<VoidVoidFunction>.allocate(capacity: 1)
ptr.initialize(to: f)
let ctx = ptr.withMemoryRebound(to:
ThickFunction<TwoCaptureContext<Int, String>>.self, capacity: 1) {
$0.pointee.context!.pointee
}
print(ctx.first.pointee.value, ctx.second.pointee.value) // 5 foo
f.f() // call the closure – mutate the captured values.
print(ctx.first.pointee.value, ctx.second.pointee.value) // 7 foob
ptr.deinitialize()
ptr.deallocate(capacity: 1)
Passing functions into parameters of generic type
You'll note that in the previous examples, we used a VoidVoidFunction wrapper for our function values. This is because otherwise, when being passed into a parameter of generic type (such as UnsafeMutablePointer's initialize(to:) method), Swift will put a function value through some reabstraction thunks in order to unify its calling convention to one where the arguments and return are passed by reference, rather than value (the relevant IR).
But now our function value has a pointer to the thunk, rather than the actual function we want to call. So how does the thunk know which function to call? The answer is simple – Swift puts the function that we want to the thunk to call in the context itself, which will therefore look like this:
// the context object for a reabstraction thunk – contains an actual function to call.
struct ReabstractionThunkContext<Context> {
// the standard reference counting header
var ref: RefCounted
// the thick function value for the thunk to call
var function: ThickFunction<Context>
}
The first thunk that we go through has 3 parameters:
A pointer to where the return value should be stored
A pointer to where the arguments for the function are located
The context object which contains the actual thick function value to call (such as shown above)
This first thunk just extracts the function value from the context, and then calls a second thunk, with 4 parameters:
A pointer to where the return value should be stored
A pointer to where the arguments for the function are located
The raw function pointer to call
The pointer to the context of the function to call
This thunk now retrieves the arguments (if any) from the argument pointer, then calls the given function pointer with these arguments, along with its context. It then stores the return value (if any) at the address of the return pointer.
Like in the previous examples, we can test this like so:
func makeClosure() -> () -> Void {
var i = 5
return { i += 2 }
}
func printSingleCapturedValue<T>(t: T) {
let ptr = UnsafeMutablePointer<T>.allocate(capacity: 1)
ptr.initialize(to: t)
let ctx = ptr.withMemoryRebound(to:
ThickFunction<ReabstractionThunkContext<Box<Int>>>.self, capacity: 1) {
// get the context from the thunk function value, which we can
// then get the actual function value from, and therefore the actual
// context object.
$0.pointee.context!.pointee.function.context!
}
// print out captured value in the context object
print(ctx.pointee.value)
ptr.deinitialize()
ptr.deallocate(capacity: 1)
}
let closure = makeClosure()
printSingleCapturedValue(t: closure) // 5
closure()
printSingleCapturedValue(t: closure) // 7
Escaping vs. non-escaping capture
When the compiler can determine that the capture of a given local variable doesn't escape the lifetime of the function it's declared in, it can optimise by promoting the value of that variable from the heap-allocated box to the stack (this is a guaranteed optimisation, and occurs in even -Onone). Then, the function's context object need only store a pointer to the given captured value on the stack, as it is guaranteed not to be needed after the function exits.
This can therefore be done when the closure(s) capturing the variable are known not to escape the lifetime of the function.
Generally, an escaping closure is one that either:
Is stored in a non-local variable (including being returned from the function).
Is captured by another escaping closure.
Is passed as an argument to a function where that parameter is either marked as #escaping, or is not of function type (note this includes composite types, such as optional function types).
So, the following are examples where the capture of a given variable can be considered not to escape the lifetime of the function:
// the parameter is non-escaping, as is of function type and is not marked #escaping.
func nonEscaping(_ f: () -> Void) {
f()
}
func bar() -> String {
var str = ""
// c doesn't escape the lifetime of bar().
let c = {
str += "c called; "
}
c();
// immediately-evaluated closure obviously doesn't escape.
{ str += "immediately-evaluated closure called; " }()
// closure passed to non-escaping function parameter, so doesn't escape.
nonEscaping {
str += "closure passed to non-escaping parameter called."
}
return str
}
In this example, because str is only ever captured by closures that are known not to escape the lifetime of the function bar(), the compiler can optimise by storing the value of str on the stack, with the context objects storing only a pointer to it (the relevant IR).
So, the context objects for each of the closures1 will look like Box<UnsafePointer<String>>, with pointers to the string value on the stack. Although unfortunately, in a Schrödinger-like manner, attempting to observe this by allocating and re-binding a pointer (like before) triggers the compiler to treat the given closure as escaping – so we're once again looking at a Box<String> for the context.
In order to deal with the disparity between context objects that hold pointer(s) to the captured values rather than holding the values in their own heap-allocated boxes – Swift creates specialised implementations of the closures that take pointers to the captured values as arguments.
Then, a thunk is created for each closure that simply takes in a given context object, extracts the pointer(s) to the captured values from it, and passes this onto the specialised implementation of the closure. Now, we can just have a pointer to this thunk along with our context object as the thick function value.
For multiple captured values that don't escape, the additional pointers are simply added onto the end of the box, i.e
struct TwoNonEscapingCaptureContext<T, U> {
// reference counting header
var ref: RefCounted
// pointers to captured values (on the stack)...
var first: UnsafePointer<T>
var second: UnsafePointer<U>
}
This optimisation of promoting the captured values from the heap to the stack can be especially beneficial in this case, as we're no longer having to allocate separate boxes for each value – such as was the case previously.
Furthermore it's worth noting that lots of cases with non-escaping closure capture can be optimised much more aggressively in -O builds with inlining, which can result in context objects being optimised away entirely.
1. Immediately-evaluated closures actually don't use a context object, the pointer(s) to the captured values are just passed directly to it upon calling.
I am trying to see if I can use structs for my model and was trying this. When I call vm.testClosure(), it does not change the value of x and I am not sure why.
struct Model
{
var x = 10.0
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
testClosure =
{
() -> () in
model.x = 30.5
}
}
}
var vm = ViewModel(model:&m)
m.x
vm.testClosure()
m.x
An inout argument isn't a reference to a value type – it's simply a shadow copy of that value type, that is written back to the caller's value when the function returns.
What's happening in your code is that your inout variable is escaping the lifetime of the function (by being captured in a closure that is then stored) – meaning that any changes to the inout variable after the function has returned will never be reflected outside that closure.
Due to this common misconception about inout arguments, there has been a Swift Evolution proposal for only allowing inout arguments to be captured by #noescape closures. As of Swift 3, your current code will no longer compile.
If you really need to be passing around references in your code – then you should be using reference types (make your Model a class). Although I suspect that you'll probably be able to refactor your logic to avoid passing around references in the first place (however without seeing your actual code, it's impossible to advise).
(Edit: Since posting this answer, inout parameters can now be compiled as a pass-by-reference, which can be seen by looking at the SIL or IR emitted. However you are unable to treat them as such due to the fact that there's no guarantee whatsoever that the caller's value will remain valid after the function call.)
Instances of the closure will get their own, independent copy of the captured value that it, and only it, can alter. The value is captured in the time of executing the closure. Let see your slightly modified code
struct Model
{
var x = 10.0
mutating func modifyX(newValue: Double) {
let this = self
let model = m
x = newValue
// put breakpoint here
//(lldb) po model
//▿ Model
// - x : 30.0
//
//(lldb) po self
//▿ Model
// - x : 301.0
//
//(lldb) po this
//▿ Model
// - x : 30.0
}
}
var m = Model()
class ViewModel
{
let testClosure:() -> ()
init(inout model: Model)
{
model.x = 50
testClosure =
{ () -> () in
model.modifyX(301)
}
model.x = 30
}
}
let mx = m.x
vm.testClosure()
let mx2 = m.x
Here is what Apple says about that.
Classes and Structures
A value type is a type that is copied when it is assigned to a
variable or constant, or when it is passed to a function. [...] All
structures and enumerations are value types in Swift
Methods
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or
enumeration within a particular method, you can opt in to mutating
behaviour for that method. The method can then mutate (that is,
change) its properties from within the method, and any changes that it
makes are written back to the original structure when the method ends.
The method can also assign a completely new instance to its implicit
self property, and this new instance will replace the existing one
when the method ends.
Taken from here