Reputation: 4879
In struct type, mutating self in async process makes error as below.
closure cannot implicitly captured a mutating self
If I change the struct to class type, the error disappear.
What is difference between struct
and class
when mutate self in asynchronously?
struct Media {
static let loadedDataNoti = "loadedDataNotification"
let imagePath: String
let originalPath: String
let description: String
var imageData: Data?
let tag: String
var likeCount: Int?
var commentCount: Int?
var username: String?
var delegate: MediaDelegate?
public init(imagePath: String, originalPath: String, description: String, tag: String, imageData: Data? = nil) {
self.imagePath = imagePath
self.originalPath = originalPath
self.description = description
self.tag = tag
if imageData != nil {
self.imageData = imageData
} else {
loadImageData()
}
}
mutating func loadImageData() {
if let url = URL(string: imagePath) {
Data.getDataFromUrl(url: url, completion: { (data, response, error) in
if (error != nil) {
print(error.debugDescription)
return
}
if data != nil {
self.imageData = data! // Error: closure cannot implicitly captured a mutating self
NotificationCenter.default.post(name: NSNotification.Name(rawValue: Media.loadedDataNoti), object: data)
}
})
}
}
Upvotes: 3
Views: 963
Reputation: 535586
A struct is a value type. How does struct mutating work? It works by making a completely new struct and substituting it for the original. Even in a simple case like this:
struct S {
var name = "matt"
}
var s = S()
s.name = "me"
... you are actually replacing one S instance by another — that is exactly why s
must be declared as var
in order to do this.
Thus, when you capture a struct's self
into an asynchronously executed closure and ask to mutate it, you are threatening to appear at some future time and suddenly rip away the existing struct and replace it by another one in the middle of executing this very code. That is an incoherent concept and the compiler rightly stops you. It is incoherent especially because how do you even know that this same self
will even exist at that time? An intervening mutation may have destroyed and replaced it.
Thus, this is legal:
struct S {
var name = "matt"
mutating func change() {self.name = "me"}
}
But this is not:
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}
struct S {
var name = "matt"
mutating func change() {delay(1) {self.name = "me"}} // error
}
Upvotes: 7
Reputation: 2735
When you mutate an instance of a value type -- such as a struct -- you're conceptually replacing it with a new instance of the same type, i.e. doing this:
myMedia.mutatingFuncToLoadImageData()
...can be thought of as doing something like this:
myMedia = Media(withLoadedData: theDownloadedData)
...except you don't see the assignment in code.
You're effectively replacing the instance that you call a mutating
function on. In this case myMedia
. As you may realize, the mutation has to have finished at the end of the mutating function for this to work, or your instance would keep changing after calling the mutating function.
You're handing off a reference to self
to an asynchronous function that will try to mutate your instance after your mutating function has ended.
You could compile your code by doing something like
var myself = self // making a copy of self
let closure = {
myself.myThing = "thing"
}
but that would only change the value of the variable myself
, and not affect anything outside of your function.
Upvotes: 1