在Swift编程语言中,它说:
“Swift整数,浮点数,布尔值,字符串,数组和字典 - 中的所有基本类型都是值类型,并且是作为幕后结构实现。“
“结构实例始终按值传递,类实例始终通过引用传递”
“如果将Array实例分配给常量或变量,或将Array实例作为参数传递给函数或方法调用,则不会复制数组内容 at分配或呼叫发生的点。相反,两个数组共享相同的元素值序列。当您通过一个数组修改元素值时,结果可以通过另一个数组观察到。“
答案 0 :(得分:8)
数组是特殊情况。它们是具有特殊行为的值类型。
在您找到第3点的页面上,有足够的信息可以理解原因:
Swift的Array类型的赋值和复制行为更多 比其词典类型复杂。数组提供类似C的 使用数组的内容并复制时的性能 数组的内容仅在需要复制时
并且,在您声明后立即:
相反,两个数组共享相同的元素值序列。什么时候 你通过一个数组修改一个元素值,结果是 通过对方可以观察到。
对于数组,仅在执行操作时进行复制 有可能修改数组的长度。
所以,阅读这些观点我们理解:
最后一件事:请记住,复制数组时,只复制值类型(结构,枚举,基本类型)。不复制对象,只复制其引用(指针)
答案 1 :(得分:2)
基于#2 和#3 之间的干扰,我试图通过实践中的示例找到问题的答案。它真的只基于 Swift 类型。
我这里有一个Array
,我用随机数填写。
var arrayOfNumbers: Array<UInt32> = Array()
for _ in 0..100 {
arrayOfNumbers.append(arc4random())
}
我会检查它返回的内容:
let myReturnedArray: Array<UInt32> = self.myReferenceTest(arrayOfNumbers, referenceArray: &arrayOfNumbers)
if arrayOfNumbers === myReturnedArray {
println("(rS)") // returned value is the same instance
} else {
println("(rD)") // returned value is a different instance
}
我有一个带有两个参数的测试方法,第一个是Array
本身,第二个只是同一个Array
的引用。我试图用那种方法做不同的事情来看看会发生什么。
func myReferenceTest (directory: Array<UInt32, Inuit referenceArray: Array<UInt32) -> Array<UInt32> {
if directArray === referenceArray {
println("(pS)") // the same instance...
} else {
println("(pD)") // different instance ...
}
return directArray
}
将打印"(pS)"
,因此看起来引用已作为第一个参数传递而不是结构的副本。 consol说"(rS)"
,返回的值与引用相同,而不是副本。
func myReferenceTest(directArray: Array<UInt32>, inout referenceArray: Array<UInt32>) -> Array<UInt32> {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
directArray[0] = 12
return directArray
}
它仍然表示相同的"pS"
和"rS"
,但如果我打印原始数组的[0]
元素,它会更新为12
,但是directArray
是根本不是inout
paremeter。 引用被传递,并且返回了引用,我还在数组上调用了非变异方法,并且我能够进行某些更改。
func myReferenceTest(directArray: Array<UInt32>, inout referenceArray: Array<UInt32>) -> Array<UInt32> {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
var myOtherArray = directArray
myOtherArray.append(arc4random())
return myOtherArray
}
控制台说"pS"
但"rD"
因为我在mutating
中调用了导致数组被复制的directArray
方法(O(n)
),我在另一个实例上做了更改。
func myReferenceTest(directArray: Array<UInt32>, inout referenceArray: Array<UInt32>) -> Array<UInt32> {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
var myOtherArray = directArray
myOtherArray.append(arc4random())
referenceArray = myOtherArray
return myOtherArray
}
与最近的版本相同,但会再次在控制台"pS"
和"rS"
上说明。所以似乎返回了引用,而不是myOtherArray
的副本。
func myReferenceTest(directArray: Array<UInt32>, inout referenceArray: Array<UInt32>) -> Array<UInt32> {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
referenceArray.append(arc4random())
directArray[0] = 12
return directArray
}
它会再次显示"pS"
和"rD"
,参考数组的第一个元素是12
,看起来directArray
在我调用{{}后被复制了在mutating
上的1}}方法。您可以毫无疑问地检查它:referenceArray
仍然与原始数组相同,但referenceArray
现在不同了。
如果我没有返回当方法超出其范围时将要释放的directArray
,那么只能在方法的范围内创建副本。
当您在其上调用directArray
方法时,Array
似乎总是创建一个新的实例,这与{em> Swift 关于{的文档一致{1}}关键字:
但是,如果需要在特定方法中修改结构或枚举的属性,则可以选择变异该方法的行为。然后,该方法可以从方法中改变(即更改)其属性,并且当方法结束时,它所做的任何更改都将写回原始结构。该方法还可以为其隐式
mutating
属性分配一个全新的实例,并且该新实例将在方法结束时替换现有实例。
(source)
这种行为与这里的 Swift 逻辑非常相似:编译器试图不通过复制任何对象来扩展代码,直到它肯定是必要的,这看起来是{{{ 1}}至少 - 在对象上调用mutating
方法时感到高兴。
您可以在 Swift标准库参考,here中找到有关 Swift 类型self
的哪些方法的更多信息。
然而,即使Array
看起来被定义为 struct ,它也会像类一样行事,因为它们有一些类级{ {3}}:
- 类型转换使您可以在运行时检查和解释类实例的类型。
- 引用计数允许对类实例进行多次引用。
我没有找到证据证明其余两个类的特权,但是这两个类级别的特权表明mutating
肯定是更多而不是简单的mutating
,使声明#1 不明确。
有这些prvileges表明在每种情况下都会传递(逻辑上)引用。
Array
是一个最简单的情况,因为它在幕后看起来很简单Array
,当我通过struct
询问班级名称时,它显然是指一个可变的实例来自Obj-C的字典,即使Dictionary
的声明表示NSMutableDictionary
,也绝不是object_getClassName(...)
。
进一步猜测引擎中发生了什么是无用的,编译器仍然是 Beta ,所以我们不知道最终编译器将如何工作;在很多情况下,可以定义许多关于它们的理论 - (或者已经过去了),我试图只关注那些在背景中识别它们时可以证明的事实,这些事实可能会在将来改变,但那些是事实。目前。
答案 2 :(得分:1)
数组只是值类型。那是正确的。并且很可能使用COW(Copy-On-Write)来避免暴力值类型语义实现的高复制成本。因此,只有在新实例要进行变异时才会复制它。
在beta期间,这个语义问题出现了一些问题,但是在后来的测试版中它被淘汰了,因为灰色行为是疯狂的,而现在(Swift版本1.0)它是严格的值类型语义。
您的观点#3只是对优化的解释 - 这是一个仅对优化有意义的实现细节。在考虑语义时忘记实现细节。就像使用任何其他值类型的东西一样使用数组和字典。并记住实施细节仅在时需要关注性能和优化。在大多数情况下,只需使用在语义或性能方面都很好。
BTW,引用类型表示它具有可以引用的自动/隐式标识。
答案 3 :(得分:0)
swift中的数组是值类型:
var myArray1 = [1, 2, 3]
var myArray2 = myArray1
myArray2 += 4;
println(myArray1);
println(myArray2);
它为myArray1和我的Array2打印不同的值。如果它们共享相同的引用,则该值必须相同。
答案 4 :(得分:0)
Array是struct:
struct Array<T> : MutableCollection, Sliceable {...}
在代码中传递所有结构时,它们总是被复制。 类通过引用传递。
所以Array是值类型。
答案 5 :(得分:0)
Array确实是一个struct,一个值类型。然而,他们竭尽全力让它的行为类似于引用类型(特殊的复制行为,下标甚至可以修改常量数组,甚至可以为数组定义===
运算符!)。
这可能是由于性能原因造成的,但由此产生的行为是错误的并且难以使用。
引用Apple(https://devforums.apple.com/thread/228695)
在Beta 1时,数组语义不断变化,并且已经过修改,以提供像Dictionary和String这样的完整值语义。这将在以后的测试版中提供。
-Chris
因此,我们可以预期未来测试版中的数组行为会发生很大变化。