【问题标题】:Save struct in background mutating function在后台变异函数中保存结构
【发布时间】:2017-07-30 18:32:13
【问题描述】:

我试图在后台保存一个结构,但我收到了这个错误:

闭包不能隐式捕获变异的 self 参数

这是我的代码:

//MARK: Parse self methods
fileprivate mutating func ParseSave(_ completionBlock:  @escaping SuccessCompletionBlock) {
    let message: PFObject = PFObject(className: "Message")

    if let id = self.id {
        //this object exit just update it
        message.objectId = id
    }

    // set attributes

    if let text = self.text {
        message["text"] = text
    }
    message["sender"] = PFUser(withoutDataWithObjectId: self.sender.id)
    message["conversation"] = PFObject(withoutDataWithClassName: "Conversation", objectId: conversationId)

    message["viewed"] = self.viewed

    message.saveInBackground { (success, error) in
        if success {
// the next 3 lines cause the error : (when I try to update the struct - self )
            self.id = message.objectId
            self.createdAt = message.createdAt ?? self.createdAt
            self.updatedAt = message.updatedAt ?? self.updatedAt

        }
        completionBlock(success, error)
    }
}

我已经检查了这些问题:1 - 2 我已经添加了@escaping 但没用。

【问题讨论】:

  • 哪一行生成错误信息? (ibutes 到底是什么?)
  • 在完成处理程序中对结构进行变异是没有意义的。完成处理程序将处理块捕获的结构副本,因此块内所做的任何更改都不会反映在调用者中。
  • stackoverflow.com/a/41941810/1271826。或者在 Stack Overflow 中搜索“闭包不能隐式捕获变异的 self 参数”(在消息周围使用引号搜索)。以前有人问过并回答过。
  • @dan 即使我使用myMessage.ParseSave() { success, error in } myMessage 也会被完成块内的副本捕获??
  • 所以self 是一个结构?你明白,不是吗,结构突变并不真正存在?它实际上涉及复制和替换。因此,您将尝试在延迟后将 self 替换为另一个对象。这似乎不连贯。

标签: swift cocoa-touch


【解决方案1】:

我认为,如果我们尽可能少地引出您收到的错误消息,将会有所帮助。 (对于delay,请参阅dispatch_after - GCD in swift?。)

struct S {
    var name = ""
    mutating func test() {
        delay(1) {
            self.name = "Matt" // Error: Closure cannot ...
            // ... implicitly capture a mutating self parameter
        }
    }
}

原因在于结构(和枚举)突变的特殊性质:即它并不真正存在。当您设置结构的属性时,您真正要做的是复制结构实例并将其替换为另一个。这就是为什么只有 var-referenced 结构实例可以被变异的原因:引用必须是可替换的,以便实例是可变的。

现在我们可以看到我们的代码出了什么问题。显然,mutating 方法对self 进行变异是合法的;这就是mutating 的意思。但在这种情况下,我们提议离开一段时间,然后突然重新出现在现场(在这种情况下是 1 秒后)并且现在改变self。因此,我们将维护self 的副本,直到将来某个断开连接的时刻,self 会突然以某种方式被替换。这是不连贯的,尤其是因为谁知道原来的self 可能在此期间发生了变异,导致我们的副本不完美;并且编译器会阻止它。

非转义闭包不会出现同样的问题:

func f(_ f:()->()) {}

struct S {
    var name = ""
    mutating func test() {
        f {
            self.name = "Matt" // fine
        }
    }
}

那是因为闭包是非转义的;它是现在执行的,因此不存在关于未来会发生什么的不连贯性。这是转义闭包和非转义闭包之间的重要区别,也是它们区分的原因之一。

此外,类不会出现同样的问题:

class C {
    var name = ""
    func test() {
        delay(1) {
            self.name = "Matt" // fine
        }
    }
}

这是因为类实例被闭包中的reference捕获,而类实例在原地是可变的。

(也可以在这里查看我的小论文:https://stackoverflow.com/a/27366050/341994。)

【讨论】:

    猜你喜欢
    • 2023-03-06
    • 2015-12-02
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多