Modifying Struct Instance Variables Within a Dispatch Closure in Swift

Modifying struct instance variables within a Dispatch closure in Swift

I cannot test it, because I'm not using a build with that error, but I'm pretty sure by capturing self explicitly you can fix it:

dispatch_sync(connectQueue) { [self] in
self.test = 20
}

EDIT: Apparently it doesn't work, maybe you can try this (not very nice tbh):

var copy = self
dispatch_sync(connectQueue) {
copy.test = 20
}
self = copy

If you want to read more on why, here is the responsible Swift proposal.

The new dispatch API makes the sync method @noreturn so you wouldn't need the explicit capture:

connectQueue.sync {
test = 20
}

Swift 2: mutating method on struct not working from inside closure

This seems to work ok:

dispatch_after(time, highQ, { self.callChangeTimeStamp() })

Modifying struct instance using call to name in function parameter

This is an attempt to explain what I had in mind when writing my comment above.
Because there's an asynchronous call to findObjectsInBackgroundWithBlock, the inout won't help you here. The idea is to add a callback fetched like this:

func fetchStats(name: String, var nameOfClass: unit, fetched: unit -> ()) {
// your code as above
query.findObjectsInBackgroundWithBlock {
// your code as above plus the following statement:
fetched(nameOfClass)
}
}

This can be called with

fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
nameOfClass = newNameOfClass
}

(all of this code has not been tested)

The point is that you understand that your code is asynchronous (I know, I'm repeating myself). After you have called fetchStats you don't know when the callback (here: the assignment nameOfClass = newNameOfClass) will be executed. You cannot assume the assignment has been done after fetchStats has returned.

So whatever you need to do with the changed nameOfClass: the corresponding statements must go into the callback:

fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
// do whatever you want with the received newNameOfClass
}

Hope this helps.

How to return value from a closure in a Struct in Swift?

You're seeing this error because the closure captures an immutable self.

Just like primitive types (e.g. Int), structs are value-types, and Swift is built with the notion of immutability of value-types.

In other words, if you had let questionManager = QuestionManager(), you'd expect questionManager not to change. Even if it was a var, it can only mutate via direct action by the caller, e.g. questionManager.doMutatingFunc().

But, if a closure was allowed to capture self, it could modify itself at some later point. This is not allowed.

This simplest (only?) way to fix this is to turn QuestionManager into a class:

class QuestionManager {
// ...
}

How to define a custom closure that should run when an instance of a struct that uses a custom initializer is run (SwiftUI)

If you add explicit initializer then all properties you want to configure should be in that initializer as well

So the solution is

struct MapView: UIViewRepresentable {
@Binding var showMoreDetails: Bool
var VModel: ViewModel
var token: String
var configure: (MGLMapView) -> () // declare

init(showMoreDetails: Binding<Bool>, VModel: ViewModel, token: String,
configure: @escaping (MGLMapView) -> () = { _ in }) { // last
self.VModel = VModel
self.token = token
self.configure = configure // << initialize !!
_showMoreDetails = showMoreDetails
}

// .. other code

Save struct in background mutating function

I think it will help if we minimally elicit the error message you're getting. (For delay, see dispatch_after - GCD in swift?.)

struct S {
var name = ""
mutating func test() {
delay(1) {
self.name = "Matt" // Error: Closure cannot ...
// ... implicitly capture a mutating self parameter
}
}
}

The reason lies in the peculiar nature of struct (and enum) mutation: namely, it doesn't really exist. When you set a property of a struct, what you're really doing is copying the struct instance and replacing it with another. That is why only a var-referenced struct instance can be mutated: the reference must be replaceable in order for the instance to be mutable.

Now we can see what's wrong with our code. Obviously it is legal for a mutating method to mutate self; that is what mutating means. But in this case we are offering to go away for a while and then suddenly reappear on the scene (after 1 second, in this case) and now mutate self. So we are going to maintain a copy of self until some disconnected moment in the future, when self will suddenly be somehow replaced. That is incoherent, not least because who knows how the original self may have been mutated in the meantime, rendering our copy imperfect; and the compiler prevents it.

The same issue does not arise with a nonescaping closure:

func f(_ f:()->()) {}

struct S {
var name = ""
mutating func test() {
f {
self.name = "Matt" // fine
}
}
}

That's because the closure is nonescaping; it is executed now, so the incoherency about what will happen in the future is absent. This is an important difference between escaping and nonescaping closures, and is one of the reasons why they are differentiated.

Also, the same issue does not arise with a class:

class C {
var name = ""
func test() {
delay(1) {
self.name = "Matt" // fine
}
}
}

That's because the class instance is captured by reference in the closure, and a class instance is mutable in place.

(See also my little essay here: https://stackoverflow.com/a/27366050/341994.)

How can you initialize a struct with a closure parameter like this?

This works just like any function whose last parameter is a function. Trailing closure syntax is trailing closure syntax. The fact that the function is an initializer changes nothing.

So let me build up to it in stages. You know you can say:

func myfunc(whatever: () -> ()) {}
myfunc {}

Okay, but now let's make it a static method:

struct S {
static func myfunc(whatever: () -> ()) {}
}
S.myfunc {}

OK, but init is a static method — it's just a static method whose name you are allowed to omit:

struct S {
let whatever: () -> ()
}
S {} // meaning S.init {}


Related Topics



Leave a reply



Submit