I have the following code I'm using with SwiftUI:
import Foundation
public struct Trigger {
public var value = false
public mutating func toggle() {
value = true
let responseDate = Date().advanced(by: 3)
OperationQueue.main.schedule(after: .init(responseDate)) {
moveBack()
}
}
private mutating func moveBack() {
value = false
}
}
However, I'm getting an error:
Escaping closure captures mutating 'self' parameter
I understand that changing the struct to a class would solve this issue, but is there any way to actually capture a mutating self in an escaping closure in a struct?
As you have found, the quick solution is to use a reference type , a class . But why is this the case?
Swift structs are value types, so they are immutable. You can mark a function as mutating
to indicate to the compiler that a function mutates the struct, but what does that actually mean?
Consider a simple struct:
struct Counter {
var count
init(_ count: Int = 0)
{
self.count = count
}
mutating func increment() {
self.count+=1
}
}
Now, try and assign an instance of this to a let
constant:
let someCounter = Counter()
someCounter.increment()
print(someCounter.count)
You will get an error; you need to use a var
.
var someCounter = Counter()
someCounter.increment()
print(someCounter.count)
What actually happens when you call a mutating
func is that a new Counter
is created, with the new count
and it is assigned to someCounter
. It is effectively saying someCounter = Counter(someCounter.count+1)
Now, think what would happen if you could mutate self
in an escaping closure - That new Counter
is going to be created at some unspecified time in the future, but execution has already moved on. It is too late to update someCounter
.
The other advantage of using a class
, as you have found, is that you can use ObservableObject
, which makes updating your SwiftUI views much easier.
Solution I finished with:
import Foundation
import Combine
public final class IntervalTrigger: ObservableObject {
private let timeInterval: TimeInterval
@Published var value = false
public init(_ timeInterval: TimeInterval) {
self.timeInterval = timeInterval
}
public func toggle() {
value = true
let responseDate = Date().advanced(by: timeInterval)
OperationQueue.main.schedule(after: .init(responseDate)) { [weak self] in
self?.value = false
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.