Inout in swift 和引用类型
Inout in swift and reference type
我正在尝试了解值和引用类型之间的区别。现在我想使用苹果指南中的功能:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
如果我想使用这个函数,我会写这段代码
swapTwoInts{&firstIntStruct, &secondIntStruct}
我明白了,这个函数必须是引用类型,但是Int是值类型,所以我们用&。
另一方面,当我尝试在交换函数中将 Int 更改为我的 class 时,我还必须在 class 的实例之前写 &。
为什么我必须这样做,如果它已经是参考了?
假设我们编写了您正在谈论的假设函数:
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
}
但现在的问题是我们正在改变我们的副本,而不是我们的调用者给我们的原始引用。
更根本的问题是,当您将引用(指向 class 的实例,我们称之为对象)传递给函数时,引用本身会被复制(它的行为就像一个值类型)。如果该函数更改引用的值,它只会改变它自己的本地副本,正如我们所见。
当你有一个 inout C
并传入 &myObject
时,你实际传入的是 引用你对 myObject
[ 的引用=29=]。复制函数参数时,复制的是这个"ref to a ref"。然后该函数可以使用 "ref to a ref" 将新值分配给调用者具有
的引用 myObject
所以有一些较低级别的内存组件在发挥作用以充分理解这一点。
1) 当你创建一个值或引用类型时,你在堆栈上有一个新变量。该变量在值类型的情况下是实际数据,在引用类型的情况下是指向数据的指针。
2) 当你调用一个函数时,它会创建一个新的堆栈部分,并在堆栈上创建新变量(在 swift 中是 let
个实例)复制传递的变量in。因此,对于值类型,它执行深拷贝,对于引用类型,它复制指针。
那么这个意思就是当你用inout
你说的时候,取这个变量的内存地址,更新它包含的数据。所以你可以给一个值类型新数据或一个引用类型一个新的指针地址,它会在交换函数的范围之外改变。它使它成为 var
(与传入的相同)而不是正常的 let
。
我想举例说明一下。正如@Alexander 提到的,对于以 Int
作为参数的函数:
1. Pass by value
It will mutate the copies, and not the original references of caller.
More fundamentally, the issue is that when you pass a reference (to an instance of a class, which we call an object) to a function, the reference itself is copied (it behaves like a value type). If that function changes the value of the reference, it's only mutating it own local copy, as we saw.
你可以看到
func swapTwoInts(_ a: Int, _ b: Int) { }
如果改变了p和q的值,其中self.x和self.y不变。由于此函数传递的是 x 和 y 的值,而不是它们的引用。
2. Pass by reference:
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 因为 a 和 b 具有相同的 x 和 y 引用(地址)。
我正在尝试了解值和引用类型之间的区别。现在我想使用苹果指南中的功能:
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
如果我想使用这个函数,我会写这段代码
swapTwoInts{&firstIntStruct, &secondIntStruct}
我明白了,这个函数必须是引用类型,但是Int是值类型,所以我们用&。
另一方面,当我尝试在交换函数中将 Int 更改为我的 class 时,我还必须在 class 的实例之前写 &。
为什么我必须这样做,如果它已经是参考了?
假设我们编写了您正在谈论的假设函数:
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
}
但现在的问题是我们正在改变我们的副本,而不是我们的调用者给我们的原始引用。
更根本的问题是,当您将引用(指向 class 的实例,我们称之为对象)传递给函数时,引用本身会被复制(它的行为就像一个值类型)。如果该函数更改引用的值,它只会改变它自己的本地副本,正如我们所见。
当你有一个 inout C
并传入 &myObject
时,你实际传入的是 引用你对 myObject
[ 的引用=29=]。复制函数参数时,复制的是这个"ref to a ref"。然后该函数可以使用 "ref to a ref" 将新值分配给调用者具有
myObject
所以有一些较低级别的内存组件在发挥作用以充分理解这一点。
1) 当你创建一个值或引用类型时,你在堆栈上有一个新变量。该变量在值类型的情况下是实际数据,在引用类型的情况下是指向数据的指针。
2) 当你调用一个函数时,它会创建一个新的堆栈部分,并在堆栈上创建新变量(在 swift 中是 let
个实例)复制传递的变量in。因此,对于值类型,它执行深拷贝,对于引用类型,它复制指针。
那么这个意思就是当你用inout
你说的时候,取这个变量的内存地址,更新它包含的数据。所以你可以给一个值类型新数据或一个引用类型一个新的指针地址,它会在交换函数的范围之外改变。它使它成为 var
(与传入的相同)而不是正常的 let
。
我想举例说明一下。正如@Alexander 提到的,对于以 Int
作为参数的函数:
1. Pass by value
It will mutate the copies, and not the original references of caller.
More fundamentally, the issue is that when you pass a reference (to an instance of a class, which we call an object) to a function, the reference itself is copied (it behaves like a value type). If that function changes the value of the reference, it's only mutating it own local copy, as we saw.
你可以看到
func swapTwoInts(_ a: Int, _ b: Int) { }
如果改变了p和q的值,其中self.x和self.y不变。由于此函数传递的是 x 和 y 的值,而不是它们的引用。
2. Pass by reference:
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 因为 a 和 b 具有相同的 x 和 y 引用(地址)。