SwiftUI: What's 'Escaping closure captures mutating 'self' parameter' and how to fix it
EDIT:
Seems like you cannot mutate structs anymore in escaping closure without removing @escaping which not be possible in your case. You might want to consider changing your implementation to a class.
Structs are immutable. Which mean they cannot be mutated. In your case you are modifying the value of self.bool1 = true
which is changing the value of self.
~~A better way (IMO) would be to create a mutating func
to do your firebase call and update the values inside mutating function.~~
Also, you shouldn’t use State property wrappers in models. They should only be used in views.
Closure cannot implicitly capture a mutating self parameter
The short version
The type owning your call to FirebaseRef.observeSingleEvent(of:with:)
is most likely a value type (a struct
?), in which case a mutating context may not explicitly capture self
in an @escaping
closure.
The simple solution is to update your owning type to a reference once (class
).
The longer version
The observeSingleEvent(of:with:)
method of Firebase is declared as follows
func observeSingleEvent(of eventType: FIRDataEventType,
with block: @escaping (FIRDataSnapshot) -> Void)
The block
closure is marked with the @escaping
parameter attribute, which means it may escape the body of its function, and even the lifetime of self
(in your context). Using this knowledge, we construct a more minimal example which we may analyze:
struct Foo {
private func bar(with block: @escaping () -> ()) { block() }
mutating func bax() {
bar { print(self) } // this closure may outlive 'self'
/* error: closure cannot implicitly capture a
mutating self parameter */
}
}
Now, the error message becomes more telling, and we turn to the following evolution proposal was implemented in Swift 3:
- SE-0035: Limiting inout capture to
@noescape
contexts
Stating [emphasis mine]:
Capturing an
inout
parameter, includingself
in a mutating
method, becomes an error in an escapable closure literal, unless the
capture is made explicit (and thereby immutable).
Now, this is a key point. For a value type (e.g. struct
), which I believe is also the case for the type that owns the call to observeSingleEvent(...)
in your example, such an explicit capture is not possible, afaik (since we are working with a value type, and not a reference one).
The simplest solution to this issue would be making the type owning the observeSingleEvent(...)
a reference type, e.g. a class
, rather than a struct
:
class Foo {
init() {}
private func bar(with block: @escaping () -> ()) { block() }
func bax() {
bar { print(self) }
}
}
Just beware that this will capture self
by a strong reference; depending on your context (I haven't used Firebase myself, so I wouldn't know), you might want to explicitly capture self
weakly, e.g.
FirebaseRef.observeSingleEvent(of: .value, with: { [weak self] (snapshot) in ...
SwiftUI Escaping closure captures mutating 'self' parameter
An object's initializer cannot do anything asynchronous. Its job is to produce the object immediately, with all its properties initialized.
How exactly are structs captured in escaping closures?
While it might make sense for a struct to be copied, as your code demonstrates, it is not. That's a powerful tool. For example:
func makeCounter() -> () -> Int {
var n = 0
return {
n += 1 // This `n` is the same `n` from the outer scope
return n
}
// At this point, the scope is gone, but the `n` lives on in the closure.
}
let counter1 = makeCounter()
let counter2 = makeCounter()
print("Counter1: ", counter1(), counter1()) // Counter1: 1 2
print("Counter2: ", counter2(), counter2()) // Counter2: 1 2
print("Counter1: ", counter1(), counter1()) // Counter1: 3 4
If n
were copied into the closure, this couldn't work. The whole point is the closure captures and can modify state outside itself. This is what separates a closure (which "closes over" the scope where it was created) and an anonymous function (which does not).
(The history of the term "close over" is kind of obscure. It refers to the idea that the lambda expression's free variables have been "closed," but IMO "bound" would be a much more obvious term, and is how we describe this everywhere else. But the term "closure" has been used for decades, so here we are.)
Note that it is possible to get copy semantics. You just have to ask for it:
func foo(){
var wtf = Wtf()
DispatchQueue.global().async { [wtf] in // Make a local `let` copy
var wtf = wtf // To modify it, we need to make a `var` copy
wtf.x = 5
}
Thread.sleep(forTimeInterval: 2)
// Prints 1 as you expected
print("x = \(wtf.x)")
}
In C++, lambdas have to be explicit about how to capture values, by binding or by copying. But in Swift, they chose to make binding the default.
As to why you're allowed to access wtf
after it's been captured by the closure, that's just a lack of move semantics in Swift. There's no way in Swift today to express "this variable has been passed to something else and may no longer be accessed in this scope." That's a known limitation of the language, and a lot of work is going into fix it. See The Ownership Manifesto for more.
Swift 2: mutating method on struct not working from inside closure
This seems to work ok:
dispatch_after(time, highQ, { self.callChangeTimeStamp() })
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.)
Related Topics
iOS 7 Uiwebview Keyboard Issue
Uncaught Exception: This Class Is Not Key Value Coding-Compliant
Why Is -Diddeselectrowatindexpath Not Being Called
Rebuild an Nsarray by Grouping Objects That Have Matching Id Numbers
How to Call Presentviewcontroller from Within a Uicollectionviewcell
Error: Error Domain=Nsurlerrordomain Code=-1001 "The Request Timed Out."
Enable Application Cache in Wkwebview
Swift Version of Componentsseparatedbystring
How to Debug an iOS Extension (.Appex)
Synchronous Url Request on Swift 2
How to Run Nstimer in Background Beyond 180Sec in iOS 7
Firebase Undefined Symbols for Architecture X86_64
Error: Invalid Bitcode Version (Producer: '800.0.35.0_0' Reader: '703.0.31_0')
What Does the Text Inside Parentheses in @Interface and @Implementation Directives Mean
Open Mobile Safari from a Link in a Webview
Offscreen Uitableviewcells (For Size Calculations) Not Respecting Size Class