【问题标题】:Different process between Struct and Class in mutating asynchronously in Swift3Swift3中Struct和Class异步变异的不同过程
【发布时间】:2016-12-10 13:30:36
【问题描述】:

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

【问题讨论】:

  • 制作这个代码在mutating中的函数。喜欢这个mutating func() { //your code }
  • 函数是否声明为mutating
  • @shallowThought 我试过了。但结果是一样的。
  • 发布更多代码。
  • 添加了完整的代码。

标签: swift class asynchronous struct swift3


【解决方案1】:

结构是一种值类型。结构变异是如何工作的?它通过创建一个完全 new 结构并 替换 原始结构来工作。即使在这样的简单情况下:

    struct S {
        var name = "matt"
    }

    var s = S()
    s.name = "me"

...您实际上是在替换一个 S 实例 — 这正是为什么必须将 s 声明为 var 才能做到这一点。

因此,当您将结构的 self 捕获到异步执行的闭包中并要求对其进行变异时,您可能会在未来某个时间出现并突然撕掉现有结构并用另一个 in 替换它执行这段代码的中间。这是一个不连贯的概念,编译器正确地阻止了你。这是不连贯的,特别是因为你怎么知道那个相同的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
}

【讨论】:

  • 问题集中在不同的引用和复制方式上。谢谢。
【解决方案2】:

当你改变一个值类型的实例时——比如一个结构——你在概念上用一个相同类型的新实例来替换它,即这样做:

myMedia.mutatingFuncToLoadImageData()

...可以认为是在做这样的事情:

myMedia = Media(withLoadedData: theDownloadedData)

...除了您没有在代码中看到分配。

您实际上是在替换调用mutating 函数的实例。在这种情况下myMedia。正如您可能意识到的那样,突变必须在突变函数结束时完成才能使其工作,否则您的实例将在调用突变函数后不断变化。

您正在将对 self 的引用传递给一个异步函数,该函数将尝试在您的变异函数结束后更改您的实例

您可以通过执行类似的操作来编译您的代码

var myself = self // making a copy of self
let closure = {
    myself.myThing = "thing"
}

但这只会改变变量myself 的值,不会影响函数之外的任何内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-29
    • 2017-09-14
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    相关资源
    最近更新 更多