【问题标题】:Inout in swift and reference type快速和参考类型的输入输出
【发布时间】:2018-05-09 19:36:28
【问题描述】:

我试图了解值和引用类型之间的区别。现在我想使用苹果指南中的功能:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
  let temporaryA = a
  a = b
  b = temporaryA
}

如果我想使用这个函数,我会写这段代码

swapTwoInts{&firstIntStruct, &secondIntStruct}

我知道必须把这个函数引用类型放进去,但是Int是一个值类型,所以我们用&。
另一方面,当我尝试在交换函数中将 Int 更改为我的类时,我还必须在类的实例之前编写 &。

如果它已经是参考,为什么我必须这样做?

【问题讨论】:

    标签: swift reference inout


    【解决方案1】:

    假设我们编写了您正在谈论的假设函数:

    class C {}
    
    func swapTwoC(_ lhs: C, rhs: C) {
        let originalLHS = lhs
        lhs = rhs
        rhs = originalLHS
    }
    

    直接的问题是lhsrhs 是不可变的。要改变它们,我们需要制作可变副本:

    func swapTwoC(_ lhs: C, rhs: C) {
        var lhs = lhs; var rhs = rhs
        let originalLHS = lhs
        lhs = rhs
        rhs = originalLHS
    }
    

    但现在的问题是我们正在改变我们的副本,而不是我们的调用者给我们的原始引用。

    更根本的问题是,当您将引用(到类的实例,我们称之为对象)传递给函数时,引用本身会被复制(它的行为类似于值类型)。如果该函数改变了引用的值,它只会改变它自己的本地副本,正如我们所看到的。

    当您有一个inout C 并传入&myObject 时,您实际上传入的是一个对您对myObject 的引用的引用。当函数参数被复制时,复制的是这个“ref to a ref”。然后,该函数可以使用该“ref to a ref”将新值分配给调用者拥有的引用myObject

    【讨论】:

    • 感谢您的精彩回答!但要确认,当我使用结构(值类型)时,我只将引用类型传递给 swapTwoInts(&firstIntStruct, &secondIntStruct),而不是引用引用。它的工作原理是什么?
    • 你的意思是,inout 会在函数中创建副本?
    • @Wike 函数参数始终被复制。复制值(结构/枚举/元组的实例)或复制引用(引用类的实例)。 &firstIntStruct 是对 firstIntStruct 的引用。该引用被复制,swapTwoInts 得到它自己的副本来玩。 swapTwoInts 可以取消引用该引用以访问firstIntStruct 的内存(调用者引用的内存与firstIntStruct 相同)
    【解决方案2】:

    我想用例子来解释它。正如@Alexander 提到的,对于以Int 作为参数的函数:

    1.按值传递

    它会改变副本,而不是调用者的原始引用。

    更根本的问题是,当您将引用(到类的实例,我们称之为对象)传递给函数时,引用本身会被复制(它的行为类似于值类型)。如果该函数改变了引用的值,它只会改变它自己的本地副本,正如我们所看到的。

    你可以看到

    func swapTwoInts(_ a: Int, _ b: Int) { }

    If 改变了 p 和 q 的值,其中 self.x 和 self.y 不变。因为这个函数传递的是 x 和 y 的值而不是它们的引用。

    2。通过引用传递:

    func swapTwoInts(_ a: inout Int, _ b: inout Int) { }

    它传递了 self.x 和 self.y 的引用,这就是为什么你不必像在以前的类型中使用 p 和 q 那样再次改变它们。因为它将使用var xvar y 改变对象。

    您可以看到,a 和 b 在日志中具有引用值,并且更改 a 和 b 也改变了 self.x 和 self.y,因为 a b 与 x 和 y 具有相同的引用(地址)。

    【讨论】:

      【解决方案3】:

      因此,有一些较低级别的内存组件正在发挥作用,以充分理解这一点。

      1) 当您创建一个值或引用类型时,您在堆栈上有一个新变量。该变量要么是值类型的实际数据,要么是引用类型的数据指针。

      2) 当你调用一个函数时,它会创建堆栈的一个新部分,并在堆栈上创建新变量(在 swift 中是 let 实例)复制传入的变量。所以对于值类型它确实深拷贝,对于引用类型,它拷贝指针。

      所以这意味着当你使用inout 时,你是说,获取这个变量的内存地址并更新它包含的数据。所以你可以给一个值类型新的数据或一个引用类型一个新的指针地址,它会在交换函数的范围之外发生变化。它使它成为var(与传入的相同)而不是像正常的let

      【讨论】:

        猜你喜欢
        • 2012-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-07
        • 2011-11-11
        • 2019-07-17
        • 2012-04-02
        • 2017-09-20
        相关资源
        最近更新 更多