クラスまたはプリミティブ型を関数に渡すと、関数でパラメーターに加えられた変更はクラスの外部に反映されます。これは基本的にinout
パラメーターが行うことになっているものと同じです。
Inoutパラメータの良いユースケースは何ですか?
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)
が必要になりました。
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として動作します(参照による動作ではありません)。言語参照ドキュメントから次のステートメントを繰り返して要約します。
コピーインコピーアウトと参照による呼び出しの動作の違いに依存しないでください。
inoutパラメーターを使用すると、値型パラメーターのデータを変更し、関数呼び出しが終了した後も変更を保持できます。
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)")
}
}
基本的に、変数のアドレスで遊ぶ場合に便利です。データ構造アルゴリズムで非常に便利です。
クラスを操作する場合、あなたが言うように、パラメーターはクラスへの参照であるため、クラスを変更できます。ただし、パラメーターが値型の場合、これは機能しません( 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
}