Aus der Apple-Sprachreferenz: Deklarationen - In-Out-Parameter :
Wenn das Argument ein Wert ist, der an einer physischen Adresse im Speicher gespeichert ist, wird zur Optimierung derselbe Speicherort sowohl innerhalb als auch außerhalb des Funktionskörpers verwendet. Das optimierte Verhalten wird als Referenzaufruf bezeichnet. Es erfüllt alle Anforderungen des Copy-In-Copy-Out-Modells und beseitigt gleichzeitig den Kopieraufwand . Hängen Sie nicht von den Verhaltensunterschieden zwischen dem Einkopieren und dem Aufrufen als Referenz ab.
Wenn Sie eine Funktion haben, die einen etwas speichertechnisch großen Werttyp als Argument verwendet (z. B. einen großen Strukturtyp) und denselben Typ zurückgibt, und schließlich die Funktionsrückgabe immer nur zum Ersetzen des Aufruferarguments verwendet wird, dann inout
ist als zugehörigen Funktionsparameter zu bevorzugen.
Betrachten Sie das folgende Beispiel, in dem Kommentare beschreiben, warum wir hier inout
eine reguläre Type-in-Return-Type-Funktion verwenden möchten :
struct MyStruct {
private var myInt: Int = 1
mutating func increaseMyInt() {
myInt += 1
}
}
func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
myHugeStruct.increaseMyInt()
return myHugeStruct
}
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
myHugeStruct.increaseMyInt()
}
var a = MyStruct()
a = myFunc(a)
myFuncWithLessCopyOverhead(&a)
Im obigen Beispiel inout
kann das Ignorieren von Speicherproblemen einfach als gute Code-Praxis bevorzugt werden, um demjenigen, der unseren Code liest, mitzuteilen, dass wir &
das Argument des Funktionsaufrufers mutieren (implizit durch das kaufmännische Und vor dem Argument in der Funktion dargestellt) Anruf). Das Folgende fasst dies ziemlich gut zusammen:
Wenn eine Funktion den Wert eines Parameters ändern soll und diese Änderungen nach Beendigung des Funktionsaufrufs beibehalten werden sollen, definieren Sie diesen Parameter stattdessen als In-Out-Parameter.
Aus dem Apple Language Guide: Funktionen - In-Out-Parameter .
Einzelheiten dazu inout
und wie es tatsächlich im Speicher behandelt wird (Name copy-in-copy-out
ist etwas irreführend ...) --- zusätzlich zu den Links zum obigen Sprachhandbuch --- siehe den folgenden SO-Thread:
(Zusatz bearbeiten: Ein zusätzlicher Hinweis)
Das Beispiel in der akzeptierten Antwort von Lucas Huang oben versucht, im Rahmen der Funktion mithilfe eines inout
Arguments auf die Variablen zuzugreifen, die als inout
Argumente übergeben wurden. Dies wird nicht empfohlen und wird ausdrücklich davor gewarnt, dies in der Sprache ref:
Greifen Sie nicht auf den Wert zu, der als In-Out-Argument übergeben wurde, auch wenn das ursprüngliche Argument im aktuellen Bereich verfügbar ist . Wenn die Funktion zurückkehrt, werden Ihre Änderungen am Original mit dem Wert der Kopie überschrieben. Verlassen Sie sich nicht auf die Implementierung der Call-by-Reference-Optimierung, um zu verhindern, dass die Änderungen überschrieben werden .
Nun ist der Zugriff in diesem Fall "nur" nicht veränderlich, z. B. print(...)
aber jeder Zugriff wie dieser sollte gemäß Konvention vermieden werden.
Auf Anfrage eines Kommentators werde ich ein Beispiel hinzufügen, um zu beleuchten, warum wir mit "dem Wert, der als In-Out-Argument übergeben wurde" eigentlich nichts tun sollten .
struct MyStruct {
var myStructsIntProperty: Int = 1
mutating func myNotVeryThoughtThroughInoutFunction (inout myInt: Int) {
myStructsIntProperty += 1
myInt += 1
}
func myInoutFunction (inout myInt: Int) {
myInt += 1
}
}
var a = MyStruct()
print(a.myStructsIntProperty)
a.myInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty)
a.myNotVeryThoughtThroughInoutFunction(&a.myStructsIntProperty)
print(a.myStructsIntProperty)
In diesem Fall verhält sich das Inout also wie Copy-In-Copy-Out (und nicht als Referenz). Wir fassen zusammen, indem wir die folgende Aussage aus den Sprachreferenzdokumenten wiederholen:
Hängen Sie nicht von den Verhaltensunterschieden zwischen dem Einkopieren und dem Aufrufen als Referenz ab.