在struct类型中,异步进程中的self变异会产生如下错误。
closure cannot implicitly captured a mutating self
如果我将结构更改为类类型,则错误消失。
当异步变异自我时,struct
和class
之间有什么区别?
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)
}
})
}
}
答案 0 :(得分:1)
当您改变值类型的实例(例如结构)时,您在概念上将其替换为相同类型的新实例,即执行此操作:
myMedia.mutatingFuncToLoadImageData()
......可以被认为是这样做的:
myMedia = Media(withLoadedData: theDownloadedData)
...除了你没有在代码中看到作业。
您正在有效地替换您调用mutating
函数的实例。在这种情况下myMedia
。正如您可能已经意识到的那样,变异必须在变异函数结束时才能完成,或者您的实例在调用变异函数后会不断变化。
您将对self
的引用移交给异步函数,该异步函数将在变异函数结束后尝试改变您的实例。
您可以通过执行类似
的操作来编译代码var myself = self // making a copy of self
let closure = {
myself.myThing = "thing"
}
但这只会改变变量myself
的值,而不会影响函数之外的任何内容。
答案 1 :(得分:1)
结构是一种值类型。 struct变异是如何工作的?它的工作原理是制作一个完全新的结构,将替换为原始结构。即使在这样的简单情况下:
struct S {
var name = "matt"
}
var s = S()
s.name = "me"
...您实际上是将一个S实例替换为另一个S实例 - 这正是为什么必须将s
声明为var
才能执行此操作的原因。
因此,当你将一个结构的self
捕获到一个异步执行的闭包中并要求改变它时,你威胁要在将来出现并突然撕掉现有的结构并用另一个结构替换它>在执行此代码的过程中。这是一个不连贯的概念,编译器正确地阻止了你。它是不连贯的,特别是因为你怎么知道当时甚至会存在同样的self
?干扰突变可能已经破坏并取而代之。
因此,这是合法的:
struct S {
var name = "matt"
mutating func change() {self.name = "me"}
}
但这不是:
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
}