Swift 2使用协议扩展中的可变函数出错:“无法在不可变值上使用可变成员:'self'是不可变的”。

47

我不确定这里发生了什么事情,这似乎应该非常简单明了。我有一个含有可变变量的协议,并且有一个带有mutating函数的扩展程序。当我尝试在testClass.testFunc中使用扩展程序中声明的mtkAnimQueAppend时,会出现以下错误:"Cannot use mutating member on immutable value: 'self' is immutable.

protocol MTKAnimateValueDelegate {
    var mtkAnimQue:[MTKAnimateValue]? {get set}
}

extension MTKAnimateValueDelegate {
    ///Adds element to que
    mutating func mtkAnimQueAppend(element:MTKAnimateValue) {

        if mtkAnimQue != nil {
          mtkAnimQue?.append(element)
        } else {
          mtkAnimQue = [element]
        }
    }
}

class testClass: MTKAnimateValueDelegate {

  var mtkAnimQue:[MTKAnimateValue]?

  func testFunc() {
    var animValue = MTKAnimateValue(fromValue: 10, toValue: 20, inSeconds: 2)
    animValue.isAnimating = true
    mtkAnimQueAppend(animValue) //ERROR: "Cannot use mutating member on immutable value: 'self' is immutable
  }

}

我觉得这整个问题在很多层面上都非常难以置信。当然,你想要用Swift做的第一件事就是为视图控制器创建一个像这样的mixin。我的意思是,它是一种面向协议的语言。而iOS“就是视图控制器”。那么你会做什么呢?对我来说,令人难以置信的是,(A)苹果公司没有从一开始就解决这个问题,(B)这个问题如此晦涩难懂。应该到处有关于这个问题的10,000个QA!唉,算了。 - Fattie
1个回答

105
问题在于,在协议中将函数标记为 mutating,如果您想在结构体上使用该协议,则需要这样做。但是,传递给 testFunc 的 self 是不可变的(它是对类实例的引用),这使编译器出现了问题。如果 testClass 实际上是一个结构体,并且您可以使函数变异以解决该问题,那么这就有意义了。
我看到两个解决方法:
  1. 使协议仅限于类
  2. protocol MTKAnimateValueDelegate: class { ...
    
  3. 将testClass更改为结构体,并将testFunc标记为mutating。

无论如何,我认为这是一个需要向Apple报告的错误。

编辑

  1. 另一种解决方法是对self进行可变复制。
func testFunc() {
    var animValue = MTKAnimateValue(fromValue: 10, toValue: 20, inSeconds: 2)
    animValue.isAnimating = true
    var mutableSelf = self
    mutableSelf.mtkAnimQueAppend(animValue) 
  }

由于mutableSelf是一个引用,所以可变函数所做的任何更改仍将反映在self的状态中。


6
哦,我的天啊。我从未意识到这一点。太棒了! - devxoul
6
我希望Xcode推荐课程协议作为首要事项。感谢你的帮助。 - Aron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接