Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Self-mutate Swift struct in background thread

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)
}
like image 609
Arnold Avatar asked Sep 07 '15 04:09

Arnold


People also ask

Can you mutate a struct?

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

Can we modify struct in Swift?

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.

Why we use mutating with struct Swift?

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.

Can we use weak in struct in Swift?

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.


1 Answers

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.

like image 62
Le Mot Juiced Avatar answered Nov 13 '22 06:11

Le Mot Juiced