Mik*_*ers 90 struct value-type swift
当谈到在Swift中改变值类型时,有些事情我并不完全理解.
正如"The Swift Programming Language"iBook所述: 默认情况下,不能在其实例方法中修改值类型的属性.
因此,为了实现这一点,我们可以mutating在结构和枚举中使用关键字声明方法.
对我来说并不完全清楚的是:您可以从结构外部更改var,但不能从自己的方法中更改它.这对我来说似乎是违反直觉的,因为在面向对象语言中,你通常会尝试封装变量,因此它们只能从内部进行更改.对于结构,这似乎是另一种方式.详细说明,这是一段代码片段:
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)
Run Code Online (Sandbox Code Playgroud)
有没有人知道结构不能从他们自己的上下文中改变他们的内容的原因,而内容可以很容易地在其他地方改变?
Eon*_*nil 78
mutability属性标记在存储(常量或变量)上,而不是类型.你可以认为struct有两种模式:mutable和immutable.如果一个struct值赋给一个不可变存储(我们称之为let或不变斯威夫特)的值变为不可改变模式,你不能在值改变任何状态.(包括调用任何变异方法)
如果该值被分配给一个可变的存储(我们称之为var或可变斯威夫特),你可以自由地修改它们的状态,和变异方法的调用是允许的.
此外,类没有这种不可变/可变模式.IMO,这是因为类通常用于表示可引用的实体.可引用的实体通常是可变的,因为以适当的性能以不可变的方式制作和管理实体的参考图是非常困难的.他们可能会在以后添加此功能,但至少现在不是.
对于Objective-C程序员来说,可变/不可变的概念非常熟悉.在Objective-C中,我们为每个概念分别创建了两个类,但在Swift中,您可以使用一个结构.半工作.
对于C/C++程序员来说,这也是非常熟悉的概念.这正是const关键字在C/C++中的作用.
此外,可以非常好地优化不可变值.从理论上讲,Swift编译器(或LLVM)可以对传递的值执行copy-elision let,就像C++一样.如果你明智地使用不可变结构,它将胜过refcounted类.
正如@Joseph声称这不能提供原因,我补充一点.
结构有两种方法.简单和变异的方法.普通方法意味着不可变(或非变异).这种分离仅用于支持不可变的语义.不可变模式下的对象根本不应改变其状态.
然后,不可变方法必须保证这种语义不变性.这意味着它不应该改变任何内部价值.因此编译器不允许在不可变方法中对其自身进行任何状态更改.相反,变异方法可以自由地修改状态.
然后,您可能有一个问题,为什么不可变是默认值?这是因为很难预测变异价值的未来状态,这通常会成为头痛和错误的主要来源.许多人认为,该解决方案是避免易变的东西,然后在默认情况下不可变的是对几十年来在C/C++语言家族及其派生的愿望清单的顶部.
有关详细信息,请参阅纯功能样式.无论如何,我们仍然需要可变的东西,因为不可变的东西有一些弱点,讨论这个似乎是不可能的.
我希望这有帮助.
sup*_*cat 25
结构是字段的集合; 如果一个特定的结构实例是可变的,它的字段将是可变的; 如果实例是不可变的,则其字段将是不可变的.因此,必须为任何特定实例的字段可能是可变的或不可变的可能性准备结构类型.
为了使结构方法改变底层结构的字段,这些字段必须是可变的.如果在不可变结构上调用一个改变底层结构字段的方法,它将尝试改变不可变字段.由于没有任何好处,因此需要禁止这种调用.
为了实现这一点,Swift将结构方法分为两类:修改底层结构的方法,因此只能在可变结构实例上调用,以及那些不修改底层结构的方法,因此可以在可变和不可变实例上调用.后一种用法可能更频繁,因此是默认值.
相比之下,.NET目前(仍然!)没有提供区分修改结构的结构方法和不修改结构的结构方法的方法.相反,在不可变结构实例上调用结构方法将导致编译器生成结构实例的可变副本,让方法执行它想要的任何操作,并在方法完成时丢弃副本.这会强制编译器浪费时间复制结构,无论方法是否修改它,即使添加复制操作几乎不会将语义错误的代码转换为语义正确的代码; 它只会导致在某种程度上语义错误的代码(修改"不可变"值)以不同的方式出错(允许代码认为它正在修改结构,但丢弃尝试的更改).允许struct方法指示它们是否将修改底层结构可以消除对无用复制操作的需要,并且还确保将标记尝试的错误使用.
Le *_*ced 12
警告:前面提到了外行.
在最基本的代码级别上,这种解释并不严格.然而,一个真正在Swift工作的人已经对它进行了评估,他说这是一个很好的基本解释.
所以我想尝试简单直接地回答"为什么"的问题.
确切地说:为什么我们必须将struct函数标记为mutating在没有任何修改关键字的情况下更改struct参数?
所以,大局观,它与保持Swift 迅速的理念有很大关系.
您可以将其视为管理实际物理地址的问题.当您更改地址时,如果有很多人拥有您当前的地址,您必须通知所有人您已移动的地址.但如果没有人拥有你目前的地址,你可以随心所欲地移动,没有人需要知道.
在这种情况下,Swift有点像邮局.如果有很多有很多联系人的人移动很多,它的开销很高.它必须支付大量人员来处理所有这些通知,并且该过程需要花费大量的时间和精力.这就是为什么斯威夫特的理想状态是让镇上的每个人尽可能少接触.然后,它不需要大量的工作人员来处理地址更改,它可以更快更好地完成所有其他工作.
这也是为什么Swift-people人都在讨论价值类型与参考类型的原因.从本质上讲,引用类型遍布整个地方的"联系人",而值类型通常不需要超过一对.值类型是"Swift"-er.
所以回到小图:structs.结构在Swift中是一个大问题,因为它们可以完成对象可以做的大部分事情,但它们是值类型.
让我们通过想象一个misterStruct生活在其中的物理地址类比来继续someObjectVille.这个类比在这里有点令人兴奋,但我认为它仍然有用.
因此,为了模拟更改a上的变量struct,假设misterStruct有绿色头发,并获得一个命令切换到蓝色头发.像我说的那样,这个比喻变得令人讨厌,但有些事情发生的是,不是改变了misterStruct头发,老人走了出来,一个蓝色头发的新人进来了,那个新人开始自称misterStruct.没有人需要更改地址通知,但如果有人查看该地址,他们会看到一个蓝头发的人.
现在让我们模拟在调用函数时发生的情况struct.在这种情况下,它就像misterStruct得到一个订单,如changeYourHairBlue().因此,邮局提供的指示是misterStruct"将你的头发改成蓝色并告诉我你什么时候完成."
如果他遵循和以前一样的惯例,如果他正在做他直接改变变量时所做的事情,那么他们misterStruct将会搬出自己的房子并召唤一个蓝头发的新人.但这就是问题所在.
订单是"把你的头发变成蓝色并告诉我什么时候你完成了",但这是绿色的人得到了那个订单.在蓝人搬进来之后,仍然需要发回"工作完成"通知.但蓝衣人对此一无所知.
[为了真正想起这个比喻一些可怕的东西,绿头发的家伙在技术上发生的事情是,他搬出去后,他立即自杀了.所以他不能通知任何人任务完成!]
为了避免这个问题,在这样的情况下只,斯威夫特有直接去的房子在该地址与实际改变当前居民的头发.这是一个完全不同的过程,而不仅仅是送一个新人.
这就是Swift希望我们使用mutating关键字的原因!
最终结果看起来与任何必须引用结构的东西相同:房子的居民现在有蓝色的头发.但实现它的过程实际上是完全不同的.看起来它正在做同样的事情,但它正在做一个非常不同的事情.它正在做一件Swift一般结构化的事情.
因此,为了给可怜的编译器提供一些帮助,而不是让它必须弄清楚函数是否struct自行改变,对于每一个结构函数,我们都被要求怜悯并使用mutating关键字.
从本质上讲,为了帮助斯威夫特保持迅速,我们都必须尽自己的一份力量.:)
编辑:
嘿dude/dudette谁向我投降,我只是完全改写了我的回答.如果它与你坐得更好,你会删除downvote吗?
Swift结构可以实例化为常量(via let)或变量(via 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
Run Code Online (Sandbox Code Playgroud)
为什么追加不适用于行星名称?因为append标有mutating关键字.自从planetNames宣布使用以来let,所有标记的方法都是禁用的.
在您的示例中,编译器可以通过分配一个或多个属性来修改结构init.如果你改变你的代码一点,你会看到,x而y并不总是结构外部访问.请注意let第一行.
let p = Point(x: 1, y: 2)
p.x = 3 //error
p.moveToX(5, andY: 5) //error
Run Code Online (Sandbox Code Playgroud)
考虑一下C++的类比.Swift中的struct方法是mutating/非 - mutating类似于C++中非const/的方法const.const用C++ 标记的方法同样不能改变结构.
您可以从结构外部更改var,但不能从自己的方法更改它.
在C++中,您还可以"从结构外部更改var" - 但前提是您有非const结构变量.如果您有const结构变量,则无法分配给var,也无法调用非const方法.类似地,在Swift中,只有在struct变量不是常量时才能更改struct的属性.如果您有结构常量,则无法分配属性,也无法调用mutating方法.
| 归档时间: |
|
| 查看次数: |
37752 次 |
| 最近记录: |