Assume we have a struct capable of self-mutation that has to happen as part of a background operation:
struct Thing {
var something = 0
mutating func operation(block: () -> Void) {
// Start some background operation
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
// Mutate self upon background task completion
self.something += 1
block()
}
}
}
Now, when I use such a struct in context:
var myThing = Thing()
myThing.operation {
println(myThing.something)
}
The println
gives me 0
, as if myThing
was never mutated. Printing self.something
from within the dispatch_async
obviously yields 1
.
How do I work around this issue, preferably without having to pass the updated struct's self
in the operation
competition block and overriding the original variable in the main context?
// Ew
var myThing = Thing()
myThing.operation {
(mutatedThing) in
myThing = mutatedThing
println(myThing.something)
}
Structs are not copied on mutation. They may be mutated in-place. But every variable is a new copy (including passing the struct value as a function parameter). In functional languages values are truly immutable (so in order to change oven's temperature you need create a new oven in a new kitchen in a new house).
Similarly, in Swift, you can change a property of a struct only if the struct variable is not a constant. If you have a struct constant, you cannot assign to a property, and you also cannot call a mutating method. Save this answer.
What can a mutating function do? Essentially, a function that's been marked as mutating can change any property within its enclosing value. The word “value” is really key here, since Swift's concept of structured mutations only applies to value types, not to reference types like classes and actors.
Swift lets you mark var s in structs as weak , just like in classes. Currently this is the only way to have a collection of weak values without having one class instance per weak ref. But you have to manually filter such a collection; it doesn't auto-shrink when a reference goes away.
I'm adding a second answer because my first answer addressed a different point.
I have just encountered this difficulty myself in circumstances almost identical to yours.
After working and working and working to try to find out what was going on, and fix it, I realized that the problem was, basically, using a value type where a reference type should be used.
The closure seems to create a duplicate of the struct and operate on that, leaving the original untouched--which is more in line with the behavior of a value type.
On the other hand, the desired behavior is for the actions performed in the closure to be retained by the environment outside the closure--in other words two different contexts (inside the closure and out of it) need to refer to the same objects--which is more in line with the behavior of a reference type.
Long story short, I changed the struct to a class. The problem vanished, no other code needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With