在Swift3中异步变异的Struct和Class之间的不同过程

时间:2016-12-10 13:30:37

标签: swift class asynchronous struct swift3

在struct类型中,异步进程中的self变异会产生如下错误。

closure cannot implicitly captured a mutating self

如果我将结构更改为类类型,则错误消失。 当异步变异自我时,structclass之间有什么区别?

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)
            }
        })
    }
}

2 个答案:

答案 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
}