Self-mutate Swift struct in background thread
Asked Answered
N

2

6

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)
}
Naive answered 7/9, 2015 at 4:19 Comment(0)
T
6

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.

Tralee answered 15/10, 2015 at 0:25 Comment(2)
Yes, I also came to the conclusion that I am simply completely misusing structs and doing weird wrong stuff. Thanks for making a detailed explanation of this into an answer. For posterity: faq.sealedabstract.com/structs_or_classesNaive
This is a big issue with structs. Blocks retain values. For an ref object, it allows mutation, for a struct, it will copy and 'mutate' that copy alone. I thought of using inout for a block, but: #30020750Gezira
T
0

I have seen this exact problem many times, but without more detail I can't say if it was happening for the same reason that you are having it.

It drove me crazy until I realized that the dispatched operation was taking place at an illogical time, i.e. usually before the current time. In the dispatch block's frame of reference, it correctly updated the variable, which is why it prints out correctly from inside the block. But in the "real world", for some reason, the dispatch is considered to have never happened, and the value change is discarded. Or perhaps it's because the mutation implicitly creates a new struct and because it was "time travelling" the reference to it never updated. Can't say.

In my case, the problem went away once I correctly scheduled the dispatch. I hope that helps!

Tralee answered 22/9, 2015 at 17:24 Comment(2)
Thanks, this is definitely a great lead. What exactly do you mean by "correctly scheduling the dispatch" though? Did you set a semaphore to be notified about when it finishes?Naive
In my case correct scheduling simply means scheduling events in the future instead of the past. I was doing most of my work in Playgrounds, and trying to coordinate various moving parts that would pass around dispatch_time_t values to each other. After much hair pulling I realized that the errant closure was getting scheduled to fire using a dispatch_time_t that was, by the time the dispatch_after command received it, already in the past.Tralee

© 2022 - 2024 — McMap. All rights reserved.