Swift中的“mutating”关键字是什么意思?

25

当采用协议时,为什么在struct上实现方法前必须插入mutating关键字?

而在class中做同样的事情时为什么不需要mutating


可能是Mutating function inside class的重复问题。 - Nikos
4个回答

30

如果你要修改结构体中包含的任何状态,那么必须使用mutating关键字。由于Swift结构体是不可变对象,调用mutating函数实际上会原地返回一个新的结构体(就像将inout参数传递给函数一样)。mutating关键字让调用者知道该方法将会改变结构体的值。最好的理解方式是将结构体视为数字:如果执行操作4 + 1,4并没有变成5,只是在执行操作后得到了一个新值。mutating函数的操作也遵循同样的原则。你不能在常量上调用mutating函数(例如let someStruct = SomeStruct()),因为这相当于试图将常量分配给一个新值。由于这种行为,mutating函数只能在变量上执行(例如var someStruct = SomeStruct())。


4
这几乎是正确的。在更常见的用法中,“mutating” 允许在已有的可变结构体实例上更改属性。使用“mutating”,我们还可以将 self 重新分配给一个实例。 参见:https://docs.swift.org/swift-book/LanguageGuide/Methods.html 上的“从实例方法中修改值类型”和“在 mutating 方法内部对 self 进行赋值”。 - slythfox

15

值类型的结构体是不可变的。这意味着其他变量不能在任何给定点更改结构体实例的值。

“mutating”关键字仅在更改结构体函数内部的自身变量值时需要。

例如:

struct MyStruct {
    var abc: String = "initila value"

    func changeValue() {
        abc = "some other value". //Compile time error: Cannot assign to property: 'self' is immutable. Mark method 'mutating' to make 'self' mutable.
    }
}

由于我们试图在结构声明中定义的函数内更改变量 abc 的值,因此我们会得到编译时错误。

因此,在这里我们需要使函数可变才能在结构体内更改值。 因此,正确的代码将是:

struct MyStruct {
    var abc: String = "initila value"

    mutating func changeValue() {
        abc = "some other value"
   }
}

编辑:

在声明协议时,可以通常为引用类型和值类型声明,因此这种协议本身声明函数作为mutating,以便它可以被类和结构体都采用。

由于是引用类型,所以在类中移除(或者说不需要)mutating关键字,但是对于值类型的结构体,则需要使用mutating关键字。

来自文档:

如果您定义了一个旨在改变采用该协议的任何类型的实例的协议实例方法需求,请将该方法与mutating关键字一起标记为协议定义的一部分。这使得结构和枚举可以采用协议并满足该方法要求。

如果将协议实例方法需求标记为mutating,则在编写类的该方法实现时无需编写mutating关键字。mutating关键字仅由结构和枚举使用。

参考

希望这能解决您的疑问。


我知道在结构体和类中应该怎么做。但我想知道为什么苹果专家们设计了这样的语法,即为什么结构体隐式不可变。如果它被设计成可变的,会有什么坏处呢? - Coalabear
我已经更新了我的答案,如果需要进一步的澄清,请告诉我。 - Van
非常感谢您的回答。现在我知道它们之间的区别了,也许将来我会自己了解设计哲学。谢谢! - Coalabear

7

类是引用类型。这意味着一个变量被赋予类的类型:

let someObject = SomeClass()

仅包含对该类内存的指针,在幕后。由于它只是一个引用,所以类的内容和数据可以更改而不更改原始指针。

另一方面,结构体是值类型。如果您有一个包含结构体类型的变量:

var someStruct = SomeStruct()

这个变量实际上包含了整个结构体的数据。改变结构体的内部状态实际上涉及重新分配变量 - 因此在上面的例子中,类似someStruct.foo = "bar"这样的操作实际上会导致变量someStruct被重新分配,就好像你输入了:

someStruct = SomeStruct(foo: "bar", otherStuff: someStruct.otherStuff) // or something of this nature

这也是为什么如果计划更改结构体中的内容,就必须使用var声明结构体,而对于类则不需要这样做。
至于协议,它们可以代表结构体或类类型。因此,如果您正在处理协议存在性,则不能执行假定它是类的操作(除非协议被限制为这样)。

这是一个很好的解释。 - undefined

6
可变关键字: 如果要修改值类型的属性,必须在实例方法中使用可变关键字。使用这个关键字,您的方法就可以具有改变属性值并在方法实现结束时将其写回到原始结构的能力。
在Swift中不支持类,类是引用类型,而结构体和枚举是值类型。默认情况下,值类型的属性不能在它的实例方法中被修改。为了修改值类型的属性,必须在实例方法中使用可变关键字。

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