web-dev-qa-db-de.com

Wann Inout-Parameter verwenden?

Wenn Sie eine Klasse oder einen primitiven Typ an eine Funktion übergeben, wird jede an der Funktion vorgenommene Änderung des Parameters außerhalb der Klasse angezeigt. Dies ist im Grunde dasselbe, was ein inout-Parameter tun soll.

Was ist ein guter Anwendungsfall für einen Inout-Parameter? 

57
4thSpace

inout bedeutet, dass durch das Ändern der lokalen Variablen auch die übergebenen Parameter geändert werden. Andernfalls bleiben die übergebenen Parameter derselbe Wert. Wenn Sie inout und einen Werttyp ohne Verwendung des Referenztyps verwenden möchten, versuchen Sie, über den Referenztyp nachzudenken.

Zum Beispiel:

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)

Ein guter Anwendungsfall ist die swap-Funktion, mit der die übergebenen Parameter geändert werden.

Swift 3+ Hinweis: Ab Swift 3 muss das Schlüsselwort inoutafter den Doppelpunkt und vor dem Typ stehen. Zum Beispiel erfordert Swift 3+ jetzt func changeChar(char: inout Character).

89
Lucas Huang

Von Apple-Sprachreferenz: Deklarationen - In-Out-Parameter :

Zur Optimierung, wenn das Argument ein Wert ist, der in einem physischen .__ gespeichert ist. Adresse im Speicher wird derselbe Speicherplatz sowohl in als auch .__ verwendet. außerhalb des Funktionskörpers. Das optimierte Verhalten wird als Aufruf von .__ bezeichnet. Referenz; es erfüllt alle anforderungen des copy-in Kopiermodell , während der Overhead beim Kopieren entfernt wird. Verlassen Sie sich nicht zu den Verhaltensunterschieden zwischen Copy-In-Copy-Out und Aufruf von Referenz.

Wenn Sie über eine Funktion verfügen, die einen etwas speicherinternen großen Werttyp als Argument akzeptiert (z. B. einen großen Strukturtyp), der denselben Typ zurückgibt und schließlich die Funktion return immer nur zum Ersetzen des Aufruferarguments verwendet wird, dann inout ist als zugehöriger Funktionsparameter zu bevorzugen.

Betrachten Sie das Beispiel unten, in dem Kommentare beschreiben, warum wir inout für eine reguläre Type-In-Return-Funktion hier verwenden möchten:

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

Im obigen Beispiel kann --- das Ignorieren von Speicherproblemen ---inout einfach als eine gute Codierpraxis verwendet werden, um zu sagen, wer auch immer unseren Code gelesen hat, dass wir das Funktionsaufrufargument (implizit durch das kaufmännische Und-Zeichen &) mutieren. vor dem Argument im Funktionsaufruf). Folgendes fasst das ganz ordentlich zusammen:

Wenn Sie möchten, dass eine Funktion den Wert eines Parameters ändert, und Sie möchten Wenn diese Änderungen bestehen bleiben, nachdem der Funktionsaufruf beendet wurde, definieren Sie diesen Parameter stattdessen als In-Out-Parameter.

Von Apple-Sprachführer: Funktionen - In-Out-Parameter .


Einzelheiten zu inout und zum tatsächlichen Umgang mit dem Speicher (Name copy-in-copy-out ist etwas irreführend ...) --- zusätzlich zu den Links zum oben genannten Sprachführer --- siehe den folgenden SO - Thread :


(Zusatz hinzufügen: Eine zusätzliche Notiz)

Das in der akzeptierten Antwort von Lucas Huang angegebene Beispiel versucht im Rahmen der Funktion mit einem inout-Argument auf die Variablen zuzugreifen, die als inout-Argumente übergeben wurden. Dies wird nicht empfohlen und wird ausdrücklich in der Sprachreferenz gewarnt:

Greifen Sie nicht auf den Wert zu, der als In-Out-Argument übergeben wurde, auch wenn Das ursprüngliche Argument ist im aktuellen Gültigkeitsbereich verfügbar. Wenn der Funktion kehrt zurück, Ihre Änderungen am Original werden mit .__ überschrieben. der Wert der Kopie. Verlassen Sie sich nicht auf die Implementierung von Call-by-Reference-Optimierung, um zu verhindern, dass die Änderungen überschrieben.

Nun ist der Zugriff in diesem Fall "nur" nicht veränderlich, z. print(...), aber jeder Zugriff wie dieser sollte per Konvention vermieden werden.

Auf Anfrage eines Kommentators füge ich ein Beispiel hinzu, um herauszufinden, warum wir mit "dem als In-Out-Argument übergebenen Wert" nichts tun sollten.

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.

In diesem Fall verhält sich das Inout als Copy-In-Copy-Out (und nicht als Referenz). Wir fassen zusammen, indem wir die folgende Aussage aus den Sprachreferenzdokumenten wiederholen:

Verlassen Sie sich nicht auf die Verhaltensunterschiede zwischen Copy-In-Copy-Out und aufruf durch referenz.

32
dfri

Funktionsparameter sind standardmäßig Konstanten. Der Versuch, den Wert eines Funktionsparameters innerhalb des Rumpfes dieser Funktion zu ändern, führt zu einem Fehler bei der Kompilierung. Das bedeutet, dass Sie den Wert eines Parameters nicht versehentlich ändern können. Wenn Sie möchten, dass eine Funktion den Wert eines Parameters ändert und diese Änderungen nach Beendigung des Funktionsaufrufs bestehen bleiben, definieren Sie diesen Parameter stattdessen als In-Out-Parameter.

 see image below for Description

17

inout-Parameter ermöglichen es uns, die Daten eines Werttypparameters zu ändern und die Änderungen nach dem Funktionsaufruf zu behalten.

0
H S Progr

wenn Sie den inout-Parameter Swift 4.0 Work verwenden

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

Grundsätzlich ist es nützlich, wenn Sie mit Adressen von Variablen spielen möchten. Dies ist sehr nützlich für Datenstrukturalgorithmen

0
JeeVan TiWari

Wenn Sie mit Klassen arbeiten, können Sie, wie Sie sagen, die Klasse ändern, da der Parameter eine Referenz auf die Klasse ist. Dies funktioniert jedoch nicht, wenn Ihr Parameter ein Werttyp ist ( https://docs.Swift.org/Swift-book/LanguageGuide/Functions.html - In-Out-Parameterabschnitt).

Ein gutes Beispiel für die Verwendung von inout ist dieses (Definieren der Mathematik für CGPoints):

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