为什么在采用协议时为什么必须在结构体中实现方法之前放入“变异”,而在类中做相同的事情时却不需要?
答案 0 :(得分:3)
仅当您要更改结构中包含的任何状态时才需要mutating关键字。由于Swift结构是不可变的对象,因此调用mutating函数实际上会返回一个新的结构原位(就像将inout参数传递给函数一样)。 mutating关键字使调用者知道该方法将改变值。最好的概念化方法是将您的结构与数字相同。如果执行4 + 1操作,则4 << em> 5不成功,那么执行操作后就得到了一个新值。变异功能以相同的原理运行。如果您将值类型设为常量(例如,让someStruct = SomeStruct()),则无法在其上调用变异函数:它们只能在变量上调用。
答案 1 :(得分:2)
类是引用类型。这意味着变量是键入类的:
let someObject = SomeClass()
仅在底层包含一个指向该类内存的指针。该类的内容和数据可以更改,而无需更改原始指针,因为它只是一个引用。
另一方面,结构是值类型。如果您有一个包含结构类型的变量:var someStruct = SomeStruct()
变量本身实际上包含所有结构的数据。更改结构的内部状态实际上涉及到重新分配变量-因此,在上面的示例中,类似someStruct.foo = "bar"
的事情实际上会导致重新分配someStruct
变量,就像您键入了一样:
someStruct = SomeStruct(foo: "bar", otherStuff: someStruct.otherStuff) // or something of this nature
这也是为什么如果您打算更改结构中的任何内容,则必须使用var
来声明结构,而对于类则不是这样。
对于协议,它们可以表示结构或类类型,因此,如果您要处理协议存在性,则不能在假定它是类的情况下对其进行操作(除非协议受此约束)。 / p>
答案 2 :(得分:2)
成为值类型结构是不可变的。意味着其他变量不能在任何给定点更改结构实例的值。
仅在结构函数内部更改自变量的值时才需要使用突变字。
为。例如
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.
}
}
在这里,当我们尝试更改struct本身声明的函数中的变量abc的值时,会出现编译时错误。
因此,在这里我们需要对函数进行变异,以在结构内部更改值。因此正确的代码将是:
struct MyStruct {
var abc: String = "initila value"
mutating func changeValue() {
abc = "some other value"
}
}
编辑:
在声明协议时,可以为引用和值类型通用地声明它,因此这些类型的协议本身将函数声明为变异的,因此可以被类和结构所采用。
由于是引用类型,所以在类中删除了mutating关键字(或者可以说不是必需的),但是对于值为值类型的结构,则需要使用mutating关键字。
从文档中
如果您定义的协议实例方法要求旨在使采用该协议的任何类型的实例变异,请将该方法标记为mutating关键字,作为协议定义的一部分。这使结构和枚举可以采用该协议并满足该方法要求。
如果将协议实例方法的要求标记为mutating,则在为类编写该方法的实现时无需编写mutating关键字。 mutating关键字仅用于结构体和枚举。
我希望这会清除您的疑问。
答案 3 :(得分:0)
快速地,类是引用类型,而结构和枚举是值类型。默认情况下,不能在其实例方法中修改值类型的属性。为了修改值类型的属性,必须在实例方法中使用mutating关键字。使用此关键字,您的方法将能够更改属性的值,并在方法实现结束时将其写回到原始结构。 以下是Swift中Stack的简单实现,说明了变异函数的用法。
struct StackData {
public private(set) var itemsInStack = [Int]() // Empty items array
mutating func push(_ item: Int) {
itemsInStack.append(item)
}
mutating func pop() -> Int? {
if !itemsInStack.isEmpty {
return itemsInStack.removeLast()
}
return nil
}
}
var stack = StackData()
stack.push(4)
stack.push(78)
stack.items // [4, 78]
stack.pop()
stack.itemsInStack // [4]