【问题标题】:Passing variables by reference in Swift在 Swift 中通过引用传递变量
【发布时间】:2018-01-18 19:16:29
【问题描述】:

我开始学习 c++,现在我想知道我是否也可以在 Swift 中做一些事情。

我从未真正想过当我们将变量作为参数传递给 Swift 中的函数时会发生什么。

我们以string 类型的变量为例。

在 c++ 中,我可以通过复制参数或传递引用/指针来将参数传递给函数。

void foo(string s) 或 void foo (string& s); 在第一种情况下,我的原始变量的副本将被创建,并且 foo 将收到一个副本。在第二种情况下,我基本上是在内存中传递变量的地址而不创建副本。

现在在 Swift 中,我知道我可以将函数的参数声明为 inout,这意味着我可以修改原始对象。

1) func foo(s:String)... 2)func testPassingByReference(s: inout String)...

我对 String 做了一个扩展来打印对象的地址:

extension String {
    func address() -> String {
        return String(format: "%p", self);
    }
}

结果不是我想看到的。

var str = "Hello world"
print(str.address()) 

0x7fd6c9e04ef0

func testPassingByValue(s: String) {
    print("he address of s is: \(s.address())")
}
func testPassingByReference(s: inout String) {
    print("he address of s is: \(s.address())")
}

testPassingByValue(s: str)

0x7fd6c9e05270

testPassingByReference(s: &str)

0x7fd6c9e7caf0

我理解为什么当我们按值传递参数时地址不同,但当我们将参数作为 inout 参数传递时,这不是我所期望的。

Apple 开发者网站这样说

In Swift, Array, String, and Dictionary are all value types.

所以问题是,有没有办法避免复制我们传递给函数的对象(我可以有一个非常大的数组或字典)或者 Swift 不允许我们做这样的事情?

【问题讨论】:

    标签: swift swift3 memory-address


    【解决方案1】:

    只要你不修改它,复制数组和字符串很便宜(几乎免费)。 Swift 在 stdlib 中为这些集合实现了写时复制。这不是语言级别的功能。它实际上是为数组和字符串(以及一些其他类型)显式实现的。因此,您可以拥有多个共享相同后备存储的大型数组副本。

    inout 与“通过引用”不同。它的字面意思是“进出”。该值在函数的开头复制,然后在结尾复制回原始位置。

    Swift 的方法对于常见用途来说往往是高性能的,但 Swift 并没有像 C++ 那样做出强大的性能承诺。 (也就是说,在某些情况下,这使得 Swift 比 C++ 更快,因为 Swift 在数据结构的选择上不受限制。)作为一般规则,我发现很难推断可能的性能任意 Swift 代码。最坏情况下的性能很容易推断(假设总是发生复制),但很难确定何时会避免复制。

    【讨论】:

    • 有趣,有没有办法真正看到它们“都共享相同的后备存储”?喜欢打印地址或在调试器中的某处查看?
    • 您可以在内部_buffer 属性中四处寻找。您还可以查看array.withUnsafeBytes 并检查您从中得到的指针。它是指向内部存储的指针。但是关于它是如何工作的以及它是否可以复制的承诺并不多。可以在源码里戳一下看:github.com/apple/swift
    • 感谢您的回答。
    【解决方案2】:

    尽管inout 参数修改了用作函数输入参数的变量,但它们的工作方式与其他语言中的引用 不同。 Swift 中的行为称为copy-in copy-out 或按值调用结果。这意味着当您使用inout 参数时,在函数调用时,它的值被复制并且在函数内部,变量的本地副本被修改。在函数返回时,它会用修改后的副本值覆盖inout参数原始内存位置的值。

    您正在打印函数内部的变量地址,因此您实际上是在打印复制值的位置。在函数返回后尝试打印,您将看到您正在使用修改后的值打印原始位置。

    有关详细信息,请参阅文档的 In-Out parameters 部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-04
      相关资源
      最近更新 更多