迅速和变异的结构

当涉及到 Swift 中的变异值类型时,有些东西我并不完全理解。

正如“快速编程语言”iBook 所说: 默认情况下,值类型的属性不能从其实例方法中修改

为了实现这一点,我们可以在 structs 和 enum 中使用 mutating关键字声明方法。

我不完全清楚的是: 您可以从 struct 外部更改 var,但不能从它自己的方法更改它。对我来说,这似乎有违直觉,因为在面向对象语言中,您通常试图封装变量,这样它们只能从内部进行更改。对于 structs,情况似乎恰恰相反。为了详细说明,这里有一个代码片段:

struct Point {
var x = 0, y = 0
mutating func moveToX(x: Int, andY y:Int) { //Needs to be a mutating method in order to work
self.x = x
self.y = y
}
}


var p = Point(x: 1, y: 2)
p.x = 3 //Works from outside the struct!
p.moveToX(5, andY: 5)

有人知道为什么结构不能从它们自己的上下文中更改它们的内容,而其他地方的内容却可以轻松地更改吗?

59223 次浏览

可变性属性标记在存储器(常量或变量)上,而不是类型上。可以认为 struct 有两种模式: 易变的永恒不变。如果您将一个 struct 值分配给一个不可变存储器(在 Swift 中我们称之为 let不变) ,该值将变成不可变模式,并且您不能更改该值中的任何状态。(包括调用任何突变方法)

如果该值被分配给一个可变存储器(在 Swift 中我们称之为 var变量) ,您可以自由地修改它们的状态,并且允许调用可变方法。

此外,类没有这种不可变/可变模式。这是因为类通常用来表示 值得参考实体。可引用实体通常是可变的,因为它很难以不可变的方式创建和管理实体的引用图,而且性能也很好。他们以后可能会添加这个特性,但至少现在不会。

对于 Objective-C 程序员来说,可变/不可变的概念是非常熟悉的。在 Objective-C 中,每个概念有两个分离的类,但是在 Swift 中,您可以使用一个结构来实现这一点。半工半读。

For C/C++ programmers, this is also very familiar concept. This is exactly what const keyword do in C/C++.

此外,不可变的值可以被很好地优化。理论上,Swift 编译器(或 LLVM)可以对 let传递的值执行复制省略操作,就像 C + + 中一样。如果您明智地使用不可变结构,它的表现将优于重新计算的类。

更新

As @Joseph claimed this doesn't provide 为什么, I am adding a little more.

结构有两种方法。平淡无奇突变方法。原味的方法意味着 不变的(或不变的)。这种分离仅用于支持 永恒不变语义。处于不可变模式的对象根本不应该改变其状态。

Then, immutable methods must guarantee this 语义不变性语义不变性. Which means it shouldn't change any internal value. So compiler disallows any state changes of itself in a immutable method. In contrast, mutating methods are free to modify states.

然后,你可能会有一个关于 为什么默认值是不可变的?的问题,这是因为很难预测变异值的未来状态,这通常会成为头疼和 bug 的主要来源。许多人都认为这个解决方案是避免易变的东西,然后 immutable by default在 C/C + + 家庭语言及其派生中几十年来一直处于希望列表的首位。

有关详细信息,请参阅 纯功能风格。无论如何,我们仍然需要可变的东西,因为不可变的东西有一些弱点,讨论他们似乎是跑题了。

希望这个能帮上忙。

A structure is an aggregation of fields; if a particular structure instance is mutable, its fields will be mutable; if an instance is immutable, its fields will be immutable. A structure type must thus be prepared for the possibility that the fields of any particular instance may be mutable or immutable.

为了使结构方法变更底层结构的字段,这些字段必须是可变的。如果对不可变结构调用了变异基础结构字段的方法,它将尝试变异不可变字段。因为那样做不会有什么好结果,所以这样的祈祷应该被禁止。

为了实现这一点,Swift 将结构方法分为两类: 一类是修改底层结构的方法,因此只能在可变结构实例上调用; 另一类是不修改底层结构的方法,因此应该可以在可变和不可变实例上调用。后一种用法可能更为常见,因此是默认用法。

By comparison, .NET presently (still!) offers no means of distinguishing structure methods that modify the structure from those that don't. Instead, invoking a structure method on an immutable structure instance will cause the compiler to make a mutable copy of the structure instance, let the method do whatever it wants with it, and discard the copy when the method is done. This has the effect of forcing the compiler to waste time copying the structure whether or not the method modifies it, even though adding the copy operation will almost never turn what would be semantically-incorrect code into semantically-correct code; it will merely cause code that is semantically wrong in one way (modifying an "immutable" value) to be wrong in a different way (allowing code to think it's modifying a structure, but discarding the attempted changes). Allowing struct methods to indicate whether they will modify the underlying structure can eliminate the need for a useless copy operation, and also ensures that attempted erroneous usage will get flagged.

考虑一下与 C + + 的类比。Swift 中的 struct 方法为 mutating/not-mutating类似于 C + + 中的 struct 方法为非 const/const。类似地,C + + 中标记为 const的方法也不能改变结构。

You can change a var from outside a struct, but you cannot change it 从它自己的方法。

在 C + + 中,您还可以“从 struct 外部更改 var”——但是如果您有一个非 const struct 变量,则可以更改 只有。如果您有一个 const结构变量,您就不能赋值给 var,也不能调用非 const方法。类似地,在 Swift 中,只有当 struct 变量不是常量时,才能更改 struct 的属性。如果有一个结构常量,则不能将其赋值给属性,也不能调用 mutating方法。

Swift 结构可以实例化为常量(通过 let)或变量(通过 var)

考虑一下 Swift 的 Array结构(是的,它是一个结构)。

var petNames: [String] = ["Ruff", "Garfield", "Nemo"]
petNames.append("Harvey") // ["Ruff", "Garfield", "Nemo", "Harvey"]


let planetNames: [String] = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
planetNames.append("Pluto") //Error, sorry Pluto. No can do

为什么附录没有与行星名称一起工作?因为 append 是用 mutating关键字标记的。由于 planetNames是使用 let声明的,因此所有这样标记的方法都是禁用的。

在您的示例中,编译器可以通过指定 init之外的一个或多个属性来告诉您正在修改结构。如果稍微修改一下代码,就会发现 xy并不总是可以在结构之外访问。注意第一行的 let

let p = Point(x: 1, y: 2)
p.x = 3 //error
p.moveToX(5, andY: 5) //error

注意: 前面有外行人的术语。

这种解释在最基本的代码级别上并不完全正确。然而,它已经审查了一个家伙谁实际上工作的斯威夫特,他说,这是足够好的作为一个基本的解释。

所以我想尝试回答 简单而直接的“为什么”这个问题。

准确地说,是 为什么我们必须将 struct 函数标记为 mutating,我们可以在不修改任何关键字的情况下更改 struct 参数?

所以,从大局来看,这与保持 Swift swift.的理念有很大关系

你可以把它想象成管理实际物理地址的问题。当你改变你的地址时,如果有很多人拥有你现在的地址,你必须通知他们你已经搬家了。但如果没人知道你现在的地址,你可以随便搬家,没人需要知道。

在这种情况下,斯威夫特有点像邮局。如果很多有很多联系人的人经常搬家,那么开销就会很大。它不得不雇佣大量的人员来处理所有这些通知,这个过程需要花费大量的时间和精力。这就是为什么斯威夫特的理想状态是在其镇上的每个人都有尽可能少的接触。这样它就不需要大量的人员来处理地址更改,并且它可以更快更好地完成其他任何事情。

This is also why Swift-folks are all raving on about value types vs. reference types. By nature, reference types rack up "contacts" all over the place, and value types usually don't need more than a couple. Value types are "Swift"-er.

所以回到小图片: structs。结构在 Swift 中非常重要,因为它们可以做对象可以做的大部分事情,但它们是值类型。

让我们通过想象一个生活在 someObjectVille中的 misterStruct来继续物理地址类比。这个类比在这里有点不靠谱,但我认为它仍然是有帮助的。

So to model changing a variable on a struct, let's say misterStruct has green hair, and gets an order to switch to blue hair. The analogy gets wonked, like I said, but sort of what happens is that instead of changing misterStruct's hair, the old person moves out and a 蓝头发的新人 moves in, and that new person begins calling themselves misterStruct. No one needs to get a change of address notification, but if anyone looks at that address, they'll see a guy with blue hair.

现在让我们对在 struct上调用函数时发生的情况进行建模。在这种情况下,就像 misterStruct得到一个类似于 changeYourHairBlue()的订单。所以邮局把指令发给了 misterStruct“去把你的头发换成蓝色,弄好了告诉我。”

如果他跟以前一样如果他在做变量被直接改变时所做的事 misterStruct会做的是 搬出自己的房子调用一个蓝头发的新人。但这就是问题所在。

命令是“去把你的头发换成蓝色,然后告诉我你做完了”,但是是 绿色的人得到了这个命令。在这个蓝色的家伙搬进来之后,“任务完成”的通知仍然需要发送回去。但那个蓝人对此一无所知。

[这个比喻真的很糟糕,严格来说,绿头发的家伙在他搬出去之后,他立即自杀了。所以 不能通知任何人任务完成了 either!]

为了避免这个问题,在这样的情况下,像 只有,斯威夫特必须直接去该地址和 实际上改变了当前居民的头发的房子。这和派一个新人来完全是两码事。

这就是为什么斯威夫特希望我们使用 mutating关键字!

最终的结果对于任何必须引用 struct 的东西看起来都是一样的: 房子的居民现在有了蓝色的头发。但实现它的过程实际上是完全不同的。它看起来好像在做同样的事情,但是它做的是完全不同的事情。它做了一件事,Swift 结构通常不会这样做。

所以,为了给糟糕的编译器一点帮助,而不是让它不得不弄清楚一个函数是否会变异 struct,就其本身而言,每一个结构函数,,我们被要求同情并使用 mutating关键字。

本质上,为了帮助斯威夫特保持快速,我们都必须尽自己的一份力量。 :)

当我开始学习 Swift 的时候,我也想知道同样的事情,这些答案中的每一个,虽然可能增加了一些见解,但是它们本身就有点冗长和令人困惑。我觉得你问题的答案其实很简单。

The mutating method defined inside your struct wants permission to modify each and every instance of itself that will ever be created in the future. What if one of those instances gets assigned to an immutable constant with let? Uh oh. To protect you from yourself (and to let the editor and compiler know what you're 努力 to do), you're forced to be explicit when you want to give an instance method this sort of power.

相比之下,对结构的 outside属性的设置是在该结构的已知实例上进行的。如果它被赋值给一个常量,Xcode 会在您键入方法调用时告诉您。

当我开始更多地使用 Swift 时,这是我喜欢它的原因之一ーー当我键入错误时,它会提醒我。当然可以解决难解的 JavaScript 错误!

在 Structs 使用突变功能

Swift 程序员以这样的方式开发结构,即它的属性不能在 Sstruct 方法中修改。例如,检查下面给出的代码

struct City
{
var population : Int
func changePopulation(newpopulation : Int)
{
population = newpopulation //error: cannot modify property "popultion"
}
}
var mycity = City(population : 1000)
mycity.changePopulation(newpopulation : 2000)

在执行上面的代码时,我们得到了一个错误,因为我们试图给 Struct City 的属性总量赋予一个新的值。默认情况下,结构属性不能在其自己的方法内进行变更。这是苹果开发人员构建它的方式,因此默认情况下,这些结构将具有静态特性。

How do we solve it? What’s the alternative?

关键词:

将函数声明为 Struct 内部的变异允许我们改变 Structs 的属性。 第5行,上面的代码变成这样,

mutating changePopulation(newpopulation : Int)

现在我们可以将 新的人口的值赋给方法范围内的属性 人口

注:

let mycity = City(1000)
mycity.changePopulation(newpopulation : 2000)   //error: cannot modify property "popultion"

If we use let instead of var for Struct objects, then we can’t mutate the value of any properties, Also this is the reason why we get an error when we try to invoke mutating function using let instance. So it is better to use var whenever you are changing value of a property.

我很想听听你的意见和想法... ..。

Swift mutating struct

还有一个变种

struct MyStruct {
var myVar = "myVar"
let myLet = "myLet"
}


func testMutateString() {
//given
let myStruct = MyStruct()
    

//Change var
//when
var myStructCopy = myStruct
myStructCopy.myVar = "myVar changed 1"
    

//then
XCTAssert(myStructCopy.myVar == "myVar changed 1")
    

//Change let
//when
withUnsafeMutableBytes(of: &myStructCopy) { bytes in
let offset = MemoryLayout.offset(of: \MyStruct.myLet)!
let rawPointerToValue = bytes.baseAddress! + offset
let pointerToValue = rawPointerToValue.assumingMemoryBound(to: String.self)
pointerToValue.pointee = "myLet changed"
}
    

//then
XCTAssert(myStructCopy.myLet == "myLet changed")
}

[迅速类型]