簡體   English   中英

快速輸入和參考類型

[英]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 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的引用 復制函數參數時,復制的內容是“引用到引用”。 然后,該函數可以使用該“引用到引用”將新值分配給調用方具有的引用myObject

因此,有一些較低級別的內存組件正在發揮作用,以充分理解這一點。

1)創建值或引用類型時,堆棧上會有一個新變量。 在值類型的情況下,該變量是實際數據,在引用類型的情況下,該變量是數據的指針。

2)調用函數時,它將創建堆棧的新部分,並在堆棧上創建新的變量(即let實例迅速存儲),該變量將復制傳入的變量。因此,對於值類型,它將執行深度復制,對於引用類型它復制指針。

所以這意味着當您使用inout您要說的是獲取此變量的內存地址並更新其包含的數據。 因此,您可以為值類型提供新的數據,也可以為引用類型提供新的指針地址,它將在交換功能的范圍之外更改。 它使它成為一個var (與傳入的內容相同),而不是像普通的那樣let

我想舉例說明。 如@Alexander所述,對於將Int作為參數的函數:

1.傳遞價值

它將更改副本,而不是調用方的原始引用。

從根本上講,問題在於,當您將對函數的引用(對類的實例,我們稱為對象)傳遞給函數時,該引用本身即被復制(其行為類似於值類型)。 如前所述,如果該函數更改了引用的值,它只會使它自己的本地副本發生變異。

在此處輸入圖片說明

你可以看到

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

如果更改了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的引用(地址)。

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM