'var'-Parameter sind veraltet und werden in Swift 3 entfernt

120

Okay, ich aktualisiere Xcode einfach auf 7.3 und erhalte jetzt diese Warnung:

'var'-Parameter sind veraltet und werden in Swift 3 entfernt

So beheben Sie das Problem, wenn ich die Variable in dieser Funktion verwenden muss:

public func getQuestionList(var language: String) -> NSArray {
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}
SDW
quelle
6
Wie wäre es mitpublic func getQuestionList(inout language: String) -> NSArray
TotoroTotoro
2
Nein, dies ist kein geeigneter Ersatz. OP möchte wahrscheinlich keine getQuestionNebenwirkungen haben.
BallpointBen
5
Ich habe ehrlich gesagt keine Ahnung, warum sie überhaupt in Betracht ziehen würden, dies zu entfernen. Es war eines der Features, die Swift großartig gemacht haben!
Danny Bravo
Ich habe es nie selbst benutzt und verstehe die Aufregung nicht.
Mike Taverne
@ MikeTaverne (späte Antwort) Beachten Sie die folgende Funktion : func foo(_ bar: int) { /*use bar*/ bar+=1; foo(bar); }. Dies ist ohne var params nicht möglich. Sie müssen entweder eine separate Variable innerhalb der Funktion erstellen und den Wert kopieren oder den Parameter als inout markieren. Ersteres ist langsam, letzteres verursacht undefiniertes Verhalten. Viele Algorithmen verwenden eine solche Rekursion.
Kevin

Antworten:

82

Haben Sie versucht, eine neue Variable zuzuweisen?

public func getQuestionList(language: String) -> NSArray {
    var lang = language
    if self.data.count > 0 {
        if (lang.isEmpty) {
            lang = "NL"
        }
        return self.data.objectForKey("questionList" + lang) as! NSArray
    }

    return NSArray()
}
Garanda
quelle
11
Nicht wirklich das, was ich denke, dass die OP wollte
Schwefel
6
Ich hätte die Frage von OP genauso verstanden wie @garana. OP verwendet in ihrer Frage nicht inout, sondern mutiert lediglich eine bereits vorhandene Variable lokal .
Eric Aya
11
Eigentlich ist das die richtige Lösung. Bitte lesen
Scott Thompson
8
@ TimVermeulen Jeder möchte eine progressive Sprache verwenden. Apple kann seine Sprache auf viele Arten entwickeln, ohne die Syntax jeden Monat zu ändern. Wie Sie wissen, sind eine Menge Online-Dokument- und Codefragmente aufgrund von Apple abgelaufen oder veraltet. Entwickler müssen auf diese Site kommen, um bei vielen dummen Fragen wiederholt um Hilfe zu bitten. Die Syntax muss von Anfang an solide sein, wenn Apple möchte, dass mehr Entwickler gut darin sind.
TomSawyer
25
Verwenden Sie var language = language, wenn Sie keinen anderen Variablennamen einführen möchten (was imo der Hauptvorteil des var-Parameters war)
Harris
102

Die Diskussion über das Entfernen von Var aus einem Funktionsparameter ist in dieser Übermittlung auf GitHub: Remove Var Parameters vollständig dokumentiert

In diesem Dokument werden Sie feststellen, dass Benutzer varParameter häufig mit inoutParametern verwechseln . Ein varParameter bedeutet einfach, dass der Parameter im Kontext der Funktion veränderbar ist, während mit einem inoutParameter der Wert des Parameters am Rückkehrpunkt aus der Funktion in den Kontext des Aufrufers kopiert wird.

Der richtige Weg, um dieses Problem zu lösen, besteht darin, varaus dem Parameter zu entfernen und eine lokale varVariable einzuführen . Kopieren Sie oben in der Routine den Wert des Parameters in diese Variable.

Tr0yJ
quelle
44
Ich verstehe diese Änderung überhaupt nicht. Warum sollte es besser sein, eine weitere Zeile schreiben zu müssen, um eine veränderbare lokale Variable zu erstellen, als nur den Parameter als Variable zu definieren?
Ross Barbish
Für mich ist diese Änderung gut, weil sie Situationen aufgreift, in denen ich eine lokale Variable hätte implementieren sollen, aber nicht, weil ich den einfachen Ausweg gewählt und den (alten) Swift-Vorschlag akzeptiert habe, den Eingabeparameter zu einem var
dawid
1
Ich bin mit @RossBarbish dabei. Also ... wird dies entfernt, weil faule Entwickler nicht zwischen inout- und var-Parametern unterscheiden können? Pfff ...
Danny Bravo
1
Dies scheint schrecklich unnötig ..., sie hätten beide Optionen behalten sollen.
Oscar Gomez
1
Wahrscheinlich hat Swift hinter den Kulissen ohnehin eine lokale Variable über dem Parameter deklariert. Jetzt müssen wir es manuell machen. Keine Leistungsänderung, aber wir haben die Bequemlichkeit verloren, Anfängern mit einem einfachen Konzept zu helfen.
Mogelbuster
62

Fügen Sie einfach diese eine Zeile am Anfang der Funktion hinzu:

var language = language

und der Rest Ihres Codes kann wie folgt unverändert bleiben:

public func getQuestionList(language: String) -> NSArray {
    var language = language
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}
Harris
quelle
5
Die mit Abstand beste Antwort. Erfordert nur das Ändern einer Zeile.
BallpointBen
Aber sieht so unnatürlich aus @James
asyncwait
1
Ich denke, dies ist die beste Antwort, da es den gleichen Namen behält. Ähnlich wie in anderen gängigen Sprachen.
Eonist
1
@ RiverSatya Warum nicht einfach den Parameter direkt verwenden?
Declan McKenna
1
Wirklich ein toller Vorschlag. Wir werden es so in Swiftify implementieren :)
Crulex
13

Viele Leute schlagen einen inoutParameter vor, aber dafür sind sie wirklich nicht ausgelegt. Außerdem ist es nicht möglich, die Funktion mit einer letKonstanten oder einem String-Literal aufzurufen . Warum fügen Sie der Funktionssignatur nicht einfach den Standardwert hinzu?

public func getQuestionList(language language: String = "NL") -> NSArray {
    if data.count > 0 {
        return data.objectForKey("questionList" + language) as! NSArray
    } else {
        return NSArray()
    }
}

Stellen Sie nur sicher, dass Sie nicht getQuestionListmit der leeren Zeichenfolge aufrufen, falls Sie die Standardsprache verwenden möchten, sondern lassen Sie den Parameter weg:

let list = getQuestionList() // uses the default "NL" language
Tim Vermeulen
quelle
3
Ich verstehe auch nicht, warum alle auf die Inout-Lösung gesprungen sind, als OP das am Anfang nicht einmal benutzte ...
Eric Aya
1
Sie gingen davon aus, dass var und inout dasselbe taten.
Ryantxr
2

Swift 4

public func getQuestionList(language: inout String) -> NSArray {
    if self.data.count > 0 {
        if (language.isEmpty) {
            language = "NL"
        }
        return self.data.objectForKey("questionList" + language) as! NSArray
    }

    return NSArray()
}

getQuestionList(language: &someString)

In einigen Fällen, wie ich erfahren habe (bei komplexeren Setups mit Arrays), funktioniert das Erstellen einer neuen Eigenschaft innerhalb der Methode und das Mutieren dieser Eigenschaft möglicherweise nicht immer. Ganz zu schweigen davon, dass Sie die Methode überladen, anstatt einfach inoutan einen Parameter und &dessen Argument anzuhängen, wofür diese Syntax erstellt wurde.

bsod
quelle
1
public func getQuestionList(language: inout String) -> NSArray {
if self.data.count > 0 {
    if (language.isEmpty) {
        language = "NL"
    }
    return self.data.objectForKey("questionList" + language) as! NSArray
}

return NSArray()

}}

Abdul Rahman Khan
quelle
0

Ich denke, die Antworten von @Harris und @garanda sind der beste Ansatz.

In Ihrem Fall ist keine Var erforderlich. Sie können Folgendes tun:

public func getQuestionList(language: String) -> NSArray {
    if self.data.count > 0 {
        return self.data.objectForKey("questionList" + (language.isEmpty ? "NL" : language)) as! NSArray
    }
    return NSArray()
}
Simone Pistecchia
quelle
0

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html

In-Out-Parameter

Funktionsparameter sind standardmäßig Konstanten. Der Versuch, den Wert eines Funktionsparameters aus dem Hauptteil dieser Funktion heraus zu ändern, führt zu einem Fehler bei der Kompilierung. Dies bedeutet, dass Sie den Wert eines Parameters nicht versehentlich ändern können. 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.

Sie schreiben einen In-Out-Parameter, indem Sie das Schlüsselwort inout direkt vor dem Typ eines Parameters platzieren. Ein In-Out-Parameter hat einen Wert, der an die Funktion übergeben, von der Funktion geändert und an die Funktion zurückgegeben wird, um den ursprünglichen Wert zu ersetzen. Eine ausführliche Beschreibung des Verhaltens von In-Out-Parametern und der damit verbundenen Compiler-Optimierungen finden Sie unter In-Out-Parameter.

Sie können nur eine Variable als Argument für einen In-Out-Parameter übergeben. Sie können keine Konstante oder einen Literalwert als Argument übergeben, da Konstanten und Literale nicht geändert werden können. Sie setzen ein kaufmännisches Und (&) direkt vor den Namen einer Variablen, wenn Sie es als Argument an einen In-Out-Parameter übergeben, um anzuzeigen, dass es von der Funktion geändert werden kann.

HINWEIS

In-Out-Parameter dürfen keine Standardwerte haben, und variable Parameter können nicht als Inout markiert werden.

Hier ist ein Beispiel für eine Funktion namens swapTwoInts ( : :), die zwei In-Out-Integer-Parameter mit den Namen a und b enthält:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

Die Funktion swapTwoInts ( : :) tauscht einfach den Wert von b in a und den Wert von a in b. Die Funktion führt diesen Austausch durch, indem sie den Wert von a in einer temporären Konstante namens temporalA speichert, a den Wert von b zuweist und dann b temporärA zuweist.

Sie können die Funktion swapTwoInts ( : :) mit zwei Variablen vom Typ Int aufrufen , um deren Werte auszutauschen . Beachten Sie, dass den Namen von someInt und anotherInt ein kaufmännisches Und vorangestellt wird, wenn sie an die Funktion swapTwoInts ( : :) übergeben werden:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

Das obige Beispiel zeigt, dass die ursprünglichen Werte von someInt und anotherInt durch die swapTwoInts ( : :) , obwohl sie ursprünglich außerhalb der Funktion definiert wurden.

HINWEIS

In-Out-Parameter sind nicht dasselbe wie die Rückgabe eines Werts von einer Funktion. Das obige Beispiel für swapTwoInts definiert keinen Rückgabetyp oder einen Wert, ändert jedoch die Werte von someInt und anotherInt. In-Out-Parameter sind eine alternative Möglichkeit für eine Funktion, eine Wirkung außerhalb des Bereichs ihres Funktionskörpers zu erzielen.

Mustafa Mohammed
quelle
0

Hier ist eine andere Idee. Mein Anwendungsfall bestand darin, ein String-Array zum Anhängen herumzugeben, für das das Array veränderlich übergeben werden muss. Ich wollte auch dafür keinen Staat in meiner Klasse haben. Also habe ich eine Klasse erstellt, die das Array enthält, und diese übergeben. Abhängig von Ihrem Anwendungsfall mag es albern erscheinen, eine Klasse zu haben, die nur diese eine Variable enthält.

private class StringBuilder {
    var buffer: [String] = []

    func append(_ str: String) {
        buffer.append(str)
    }

    func toString() -> String {
        return buffer.joined()
    }
}

Ich verwende nur appendund joinedMethoden für das Array, so dass es einfach war, den Typ mit minimalen anderen Änderungen an meinem Code zu ändern.

Einige Anwendungsbeispiele:

private func writeMap(map: LevelMap, url: URL) -> Bool {
    let buffer = StringBuilder()

    if !writeHeader(map: map, buffer: buffer) {
        return false
    }
    if !writeFloors(map: map, buffer: buffer) {
        return false
    }

    let content = buffer.toString()
    do {
        try content.write(to: url, atomically: true, encoding: .utf8)
        return true
    } catch {}
    return false
}

private func writeHeader(map: LevelMap, buffer: StringBuilder) -> Bool {
    buffer.append("something here ...\n")
    return true
}
webjprgm
quelle
Meine Antwort ist, wenn Sie möchten, dass der ursprüngliche Wert, wie er vom Anrufer gesehen wird, geändert wird. Wenn Sie den Wert nur lokal neu zuweisen möchten, ihn aber nicht auf den Anrufer auswirken möchten, werden dies in den obigen Antworten behandelt.
Webjprgm