2017-08-10 115 views
3

我开始学习C++,现在我想知道我是否也可以在Swift中做一些事情。在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) ...

我做了一个扩展的字符串打印对象的地址:

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参数传递时,并不是我期望看到的。

苹果开发者网站上说,

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

所以现在的问题是,有没有办法避免抄袭,我们传递给函数的对象(我可以有一个相当大的阵列或字典)还是Swift不允许我们做这样的事情?

回答

2

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

inout与“参照”不同。这实际上是“进出”。该值在函数的开始处复制,然后在末尾复制回原始位置。

Swift的方法往往是普遍使用的表现,但Swift并没有像C++那样提供强大的性能承诺。 (也就是说,这使得Swift在某些情况下比C++更快,因为Swift在数据结构的选择上没有受到限制。)作为一般规则,我觉得很难推断可能的性能任意的Swift代码。很容易推断最糟糕的表现(只是假设副本总是发生),但很难确定何时会避免副本。

+0

有趣的是,有没有什么方法可以真正看到他们“共享相同的后备存储”?喜欢打印地址还是在调试器的某个地方看到它? – Alex

+0

你可以在内部'_buffer'属性中找到它。你也可以看看'array.withUnsafeBytes'并检查你得到的指针。这是一个指向内部存储的指针。但是,关于如何运作以及是否可以制作副本,并没有太多的承诺。您可以在源代码中查看:https://github.com/apple/swift –

+0

感谢您的答案。 – Alex

2

尽管inout参数修改了用作该函数输入参数的变量,但它们在其他语言中并不像参考那样工作正常。 Swift中的行为称为拷入拷入或按值结果拨号。这意味着当您使用inout参数时,在函数调用时,其值将被复制,并在该函数内部修改该变量的本地副本。在函数返回时,它将使用修改后的副本值覆盖inout参数原始存储位置处的值。

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

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