我试图理解值和引用类型之间的区别。现在我想使用苹果指南中的功能:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
如果我想使用这个函数我将编写此代码
swapTwoInts{&firstIntStruct, &secondIntStruct}
据我所知,我们必须在此函数中引入类型,但Int是值类型,因此我们使用&。
另一方面,当我尝试在交换函数中将Int更改为我的类时,我也必须写&在我班级的实例之前。
为什么我必须这样做,如果它已经是参考?
答案 0 :(得分:2)
假设我们编写了您正在谈论的假设函数:
class C {}
func swapTwoC(_ lhs: C, rhs: C) {
let originalLHS = lhs
lhs = rhs
rhs = originalLHS
}
当前的问题是lhs
和rhs
是不可变的。要改变它们,我们需要制作可变副本:
func swapTwoC(_ lhs: C, rhs: C) {
var lhs = lhs; var rhs = rhs
let originalLHS = lhs
lhs = rhs
rhs = originalLHS
}
但现在问题是我们正在改变我们的副本,而不是我们的来电者给我们的原始参考。
更基本的是,问题在于当您将引用(对于我们称之为对象的类的实例)传递给函数时,将复制引用本身(它的行为类似于值类型)。如果该函数改变了引用的值,它只会改变它自己的本地副本,正如我们所看到的那样。
如果您有inout C
,并且传递了&myObject
,那么您实际传入的内容是对myObject
。复制函数参数时,复制的是"参考ref"。然后,该函数可以使用"参考ref"为调用者具有的引用myObject
分配新值
答案 1 :(得分:1)
所以有一些较低级别的内存组件可以充分发挥作用。
1)创建值或引用类型时,堆栈上有一个新变量。该变量可以是值类型的实际数据,也可以是引用类型的数据指针。
2)当你调用一个函数时,它会创建一个新的堆栈部分,并在堆栈上创建新的变量(swift中的let
个实例),复制传入的变量。所以对于值类型它会执行深层复制,对于引用类型,它会复制指针。
所以这意味着当您使用inout
时,请使用此变量的内存地址并更新其包含的数据。因此,您可以为值类型新数据或引用类型提供新的指针地址,它将在交换函数范围之外更改。它使var
(与传入的内容相同)代替let
,就像正常一样。
答案 2 :(得分:1)
我想用例子解释一下。正如@Alexander所提到的,对于Int
作为参数的函数:
<强> 1。按值传递
它会改变副本,而不是调用者的原始引用。
更基本的是,问题在于当您将引用(对于我们称之为对象的类的实例)传递给函数时,将复制引用本身(它的行为类似于值类型)。如果该函数改变了引用的值,它只会改变它自己的本地副本,正如我们所看到的那样。
你可以看到
func swapTwoInts(_ a: Int, _ b: Int) { }
如果更改了 p和q 的值,则 self.x和self.y 保持不变。由于此函数传递 x和y 的值而不是它们的引用。
<强> 2。通过引用传递:
func swapTwoInts(_ a: inout Int, _ b: inout Int) { }
它传递了 self.x和self.y 的引用,这就是为什么你不必再像以前那种类型中的p和q那样改变它们。因为它会使用var x
和var y
变异对象。
您可以看到,a和b具有日志中的引用值,并且更改 a和b 也更改了 self.x和self.y ,因为和b具有相同的x和y的引用(地址)。