web-dev-qa-db-ja.com

inoutパラメータを使用する場合

クラスまたはプリミティブ型を関数に渡すと、関数でパラメーターに加えられた変更はクラスの外部に反映されます。これは基本的にinoutパラメーターが行うことになっているものと同じです。

Inoutパラメータの良いユースケースは何ですか?

64
4thSpace

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関数です。

Swift 3+注Swift 3で始まるinoutキーワードが必要ですafterコロンで、タイプの前。たとえば、Swift 3+にはfunc changeChar(char: inout Character)が必要になりました。

106
Lucas Huang

From Apple言語リファレンス:宣言-In-Outパラメーター

最適化として、引数がメモリ内の物理アドレスに格納されている値である場合、同じメモリ位置が関数本体の内側と外側の両方で使用されます。最適化された動作は、参照による呼び出しとして知られています。コピーのオーバーヘッドを除去しながら、コピーインコピーアウトモデルのすべての要件を満たします。コピーインコピーアウトと参照による呼び出しの動作の違いに依存しないでください。

引数として多少メモリに関する大きな値の型(大きな構造体型など)を取り、同じ型を返す関数があり、最後に呼び出し側の引数を置き換えるためだけに関数returnが常に使用される場合、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は、関数呼び出し元の引数を変更していることをコードを読んだ人に伝えるための適切なコードプラクティスとして単に好まれます(暗黙的にアンパサンド&関数呼び出しの引数の前に)。以下は、これを非常にきれいに要約しています。

関数でパラメーターの値を変更し、関数の呼び出しが終了した後もそれらの変更を保持したい場合は、代わりにそのパラメーターを入出力パラメーターとして定義します。

から Apple言語ガイド:関数-In-Outパラメーター


inoutおよびメモリ内で実際に処理される方法(名前copy-in-copy-outはやや誤解を招く...)の詳細については、上記の言語ガイドへのリンクに追加して、次のSOスレッドを参照してください:


(追加の編集:追加メモ)

上記のLucas Huangによる受け入れられた回答に示されている例は、inout引数を使用して関数のスコープ内で、inout引数として渡された変数にアクセスしようとします。これは推奨されておらず、言語リファレンスでは行わないように明示的に警告されています:

元の引数が現在のスコープで利用できる場合でも、in-out引数として渡された値にアクセスしないでください。関数が戻ると、元の変更はコピーの値で上書きされます。 変更が上書きされないようにするために、参照による最適化の実装に依存しないでください

さて、この場合のアクセスは「唯一」の不変です。 print(...)ですが、慣例により、このようなアクセスはすべて避ける必要があります。

コメンターからのリクエストに応じて、 "in-out引数として渡された値"で実際に何もしない理由に光を当てる例を追加します。 。

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.myNotVeryThoughtThroughInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty) // 3 or 4? prints 3.

したがって、この場合、inoutはcopy-in-copy-outとして動作します(参照による動作ではありません)。言語参照ドキュメントから次のステートメントを繰り返して要約します。

コピーインコピーアウトと参照による呼び出しの動作の違いに依存しないでください。

38
dfri

関数パラメーターはデフォルトでは定数です。関数の本体内から関数パラメーターの値を変更しようとすると、コンパイル時エラーが発生します。つまり、誤ってパラメーターの値を変更することはできません。関数でパラメーターの値を変更し、関数の呼び出しが終了した後もそれらの変更を保持する場合は、代わりにそのパラメーターを入出力パラメーターとして定義します。

see image below for Description

22

inoutパラメーターを使用すると、値型パラメーターのデータを変更し、関数呼び出しが終了した後も変更を保持できます。

0
H S Progr

inoutパラメータSwift 4.0を使用する場合

class ViewController: UIViewController {

    var total:Int = 100

    override func viewDidLoad() {
        super.viewDidLoad()
        self.paramTotal(total1: &total)
    }

    func paramTotal(total1 :inout Int) {
        total1 = 111
        print("Total1 ==> \(total1)")
        print("Total ==> \(total)")
    }
}
0
user3263340

基本的に、変数のアドレスで遊ぶ場合に便利です。データ構造アルゴリズムで非常に便利です。

0
JeeVan TiWari

クラスを操作する場合、あなたが言うように、パラメーターはクラスへの参照であるため、クラスを変更できます。ただし、パラメーターが値型の場合、これは機能しません( https://docs.Swift.org/Swift-book/LanguageGuide/Functions.html -In-Outパラメーターセクション)

Inoutを使用する1つの良い例は次のとおりです(CGPointの数学を定義します):

func + (left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x + right.x, y: left.y + right.y)
}

func += (left: inout CGPoint, right: CGPoint) {
  left = left + right
}
0
abanet