2017-08-10 42 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部分。