2015-12-27 32 views
21

將類或基元類型傳遞給函數時,函數中對參數所做的任何更改都會反映到類的外部。這與inout參數應該做的基本相同。何時使用inout參數?

什麼是inout參數的好用例?

+1

你確定這種情況的發生與您在傳遞的「常規」參數以及基本類型? – Thilo

+0

您是否有代碼示例顯示Swift的行爲如您所描述的那樣? – Thilo

+0

查看我的示例@Thilo –

回答

35

inout表示修改局部變量也會修改傳入的參數。沒有它,傳入的參數將保持相同的值。嘗試在使用inout時使用參考類型,而不使用值類型。

例如:

import UIKit 

var num1: Int = 1 
var char1: Character = "a" 

func changeNumber(var num: Int) { 
    num = 2 
    print(num) // 2 
    print(num1) // 1 
} 
changeNumber(num1) 

func changeChar(inout char: Character) { 
    char = "b" 
    print(char) // b 
    print(char1) // b 
} 
changeChar(&char1) 

良好的使用情況將是swap函數,它將修改傳入的參數。

斯威夫特3+注意Starting in Swift 3,在inout關鍵字一定要來冒號後和類型之前。例如,Swift 3+現在需要func changeChar(char: inout Character)

+2

這是明確定義的行爲嗎?因爲規範似乎說當函數返回時'inout'被複制回來。那麼'char1'已經是函數中的'a'了嗎? – Thilo

+0

Re:明確定義的行爲:@ drfi的回答解決了這個問題:「不要依賴於copy-in copy-out和call by reference之間的行爲差​​異」 – Thilo

+0

@Thilo你是對的,這個例子包含的代碼可能表現不好 - 定義,並且至少不符合'inout'語言參考。我會在我的答案中添加一個例子。 – dfri

18

Apple Language Reference: Declarations - In-Out Parameters

作爲一種優化,當參數是存儲在存儲器中的物理地址 的值,相同的存儲器位置所使用的函數身體外部的內部和 。優化的行爲稱爲 參考;它滿足了複製 複製模型的所有要求,同時消除了複製的開銷。不要依賴 對拷入拷貝和撥打 參考之間的行爲差​​異。

如果你有一個函數,它接受一個有些記憶明智的大值類型作爲參數(比如,一個大的結構型),並返回相同類型,最後當函數返回始終只是用來替換調用者參數,那麼inout就是優選的關聯函數參數。

考慮下面的例子中,在註釋說明爲什麼我們要使用inout比普通型的回報型功能在這裏:

struct MyStruct { 
    private var myInt: Int = 1 

    // ... lots and lots of stored properties 

    mutating func increaseMyInt() { 
     myInt += 1 
    } 
} 

/* call to function _copies_ argument to function property 'myHugeStruct' (copy 1) 
    function property is mutated 
    function returns a copy of mutated property to caller (copy 2) */ 
func myFunc(var myHugeStruct: MyStruct) -> MyStruct { 
    myHugeStruct.increaseMyInt() 
    return myHugeStruct 
} 

/* call-by-reference, no value copy overhead due to inout opimization */ 
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) { 
    myHugeStruct.increaseMyInt() 
} 

var a = MyStruct() 
a = myFunc(a) // copy, copy: overhead 
myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocation 

此外,在上面的例子---無視內存問題--- inout可以優先考慮告訴誰讀取我們的代碼,我們正在修改函數調用者參數(由函數調用中參數前面的&符號&隱式顯示)。下面總結了相當整齊:如果你想有一個函數來修改參數的值

,並希望 這些變化將持續函數調用結束後,確定 該參數爲IN-OUT參數,而不是。

Apple Language Guide: Functions - In-Out Parameters


有關inout以及如何在內存中的實際處理(名稱copy-in-copy-out有些誤導......)---在額外的鏈接到語種導遊上述---看到下面的SO線程的詳細信息:


(編輯另外:附加的註釋)

在盧卡斯黃接受的答案給出上面的例子試圖---在使用inout參數的函數的範圍---訪問作爲inout參數被傳遞,該變量。這是不推薦,並明確警告不要在語言裁判做:

不要訪問是作爲在-out參數傳遞的值,即使 原來的說法是在當前可用範圍。當 函數返回,你原來的變化是覆蓋與 複製的價值。 不要依賴於 的call-by-參考優化的實施,儘量保持變化被 覆蓋

現在,在這種情況下的訪問是「唯一的」不可變的,例如, print(...),但按照慣例,所有這樣的訪問都應該避免。

在從評論者的要求,我加入了一個例子時,爲什麼我們真的不應該做「這是作爲一個在出參數傳遞的價值」的東西閃耀光芒。

struct MyStruct { 
    var myStructsIntProperty: Int = 1 

    mutating func myNotVeryThoughtThroughInoutFunction (inout myInt: Int) { 
     myStructsIntProperty += 1 
     /* What happens here? 'myInt' inout parameter is passed to this 
      function by argument 'myStructsIntProperty' from _this_ instance 
      of the MyStruct structure. Hence, we're trying to increase the 
      value of the inout argument. Since the swift docs describe inout 
      as a "call by reference" type as well as a "copy-in-copy-out" 
      method, this behaviour is somewhat undefined (at least avoidable). 

      After the function has been called: will the value of 
      myStructsIntProperty have been increased by 1 or 2? (answer: 1) */ 
     myInt += 1 
    } 

    func myInoutFunction (inout myInt: Int) { 
     myInt += 1 
    } 
} 

var a = MyStruct() 
print(a.myStructsIntProperty) // 1 
a.myInoutFunction(&a.myStructsIntProperty) 
print(a.myStructsIntProperty) // 2 
a.myInoutFunction(&a.myStructsIntProperty) 
print(a.myStructsIntProperty) // 3 or 4? prints 3. 

因此,在這種情況下,INOUT表現爲複製在複製出(而不是由參考)。我們總結了重複從語言參考文檔以下語句:

不要依賴於複製在複製出 之間的行爲差​​異,並通過引用調用。

+0

我在理解時遇到了麻煩'不要訪問作爲輸入參數'語句傳遞的值,爲什麼會這樣呢,你能舉一個例子嗎?根據我的理解,無論inout與否,首先都會在函數內部使用一個參數,否則我們根本不需要參數。 – Wingzero

+0

當然,我會在答案中加入一個例子。 – dfri

6

功能參數默認爲常數。試圖從該函數體內更改函數參數的值會導致編譯時錯誤。這意味着你不能錯誤地改變參數的值。如果您想要一個函數來修改參數的值,並且希望這些更改在函數調用結束後保持不變,請將該參數定義爲輸入輸出參數。

see image below for Description

0

通過使用INOUT參數就可以改變一個值類型參數的數據,並隨時更改均保持不變的函數調用結束後。

相關問題