Les paramètres 'var' sont obsolètes et seront supprimés dans Swift 3

120

D'accord, je viens de mettre à jour Xcode vers la version 7.3 et maintenant je reçois cet avertissement:

Les paramètres 'var' sont obsolètes et seront supprimés dans Swift 3

Comment résoudre ce problème lorsque j'ai besoin d'utiliser le var dans cette fonction:

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
la source
6
Que diriez-vouspublic func getQuestionList(inout language: String) -> NSArray
TotoroTotoro
2
Non, ce n'est pas un remplacement approprié. OP ne veut probablement pas getQuestionavoir d'effets secondaires.
BallpointBen
5
Honnêtement, je n'ai aucune idée pourquoi ils envisageraient même de supprimer cela. C'était l'une des fonctionnalités qui rendait rapide génial!
Danny Bravo
Je ne l'ai jamais utilisé moi-même et je ne comprends pas le bruit.
Mike Taverne
@MikeTaverne (réponse tardive) Considérez la fonction suivante: func foo(_ bar: int) { /*use bar*/ bar+=1; foo(bar); }. Ceci est impossible sans var params. Vous devez soit créer une variable distincte dans la fonction et copier la valeur, soit marquer le paramètre comme inout. Le premier est lent, le second provoque un comportement indéfini. De nombreux algorithmes utilisent une récursion comme celle-ci.
kevin

Réponses:

82

Avez-vous essayé d'attribuer à une nouvelle variable

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
la source
11
Pas vraiment ce que je pense que l'OP voulait
brimstone
6
J'aurais compris la question d'OP de la même manière que @garana. OP n'utilise pas inout dans leur question, ils font simplement muter une variable préexistante localement .
Eric Aya
11
En fait, c'est la bonne solution. Veuillez consulter le numéro d'évolution de Swift qui proposait ce changement: github.com/apple/swift-evolution/blob/master/proposals/…
Scott Thompson
8
@TimVermeulen Tout le monde veut utiliser un langage progressif. Apple peut développer son langage de nombreuses manières, non en modifiant la syntaxe tous les mois. Comme vous le savez, une tonne de documents en ligne et d'extraits de code ont expiré ou sont obsolètes à cause d'Apple. Les développeurs doivent venir sur ce site pour demander de l'aide avec de nombreuses questions stupides à plusieurs reprises à cause de cela. La syntaxe doit être solide dès le début si Apple veut que plus de développeurs soient bons dans ce domaine.
TomSawyer
25
Utilisez var language = language, si vous ne voulez pas introduire un autre nom de variable (qui était le principal avantage du paramètre var en premier lieu imo)
Harris
102

La discussion sur la suppression de Var d'un paramètre de fonction est entièrement documentée dans cette soumission sur GitHub: Supprimer les paramètres de Var

Dans ce document, vous constaterez que les gens confondent souvent les varparamètres avec les inoutparamètres. Un varparamètre signifie simplement que le paramètre est modifiable dans le contexte de la fonction, tandis qu'avec un inoutparamètre, la valeur du paramètre au point de retour sera copiée hors de la fonction et dans le contexte de l'appelant.

La bonne façon de résoudre ce problème est de supprimer vardu paramètre et d'introduire une varvariable locale . En haut de la routine, copiez la valeur du paramètre dans cette variable.

Tr0yJ
la source
44
Je ne comprends pas du tout ce changement, pourquoi avoir à écrire une autre ligne pour créer un var local mutable serait mieux que de simplement définir le paramètre comme un var?
Ross Barbish
Pour moi, ce changement est bon car il détecte des situations où j'aurais dû implémenter une variable locale, mais je ne l'ai pas fait parce que j'ai choisi la solution de facilité et accepté la suggestion de (ancienne) Swift de faire du paramètre d'entrée un var
dawid
1
Je suis avec @RossBarbish à ce sujet. Donc ... cela est supprimé parce que les développeurs paresseux ne peuvent pas faire la distinction entre les paramètres inout et var? Pfff ...
Danny Bravo
1
Cela semble terriblement inutile ..., ils auraient dû garder les deux options.
Oscar Gomez
1
De toute façon, Swift déclarait probablement une variable locale au-dessus du paramètre dans les coulisses. Maintenant, nous devons le faire manuellement. Aucun changement dans les performances, mais nous avons perdu la commodité pour aider les débutants avec un concept simple.
mogelbuster
62

Ajoutez simplement cette ligne au début de la fonction:

var language = language

et le reste de votre code peut rester inchangé, comme ceci:

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
la source
5
La meilleure réponse de loin. Nécessite seulement de changer une ligne.
BallpointBen
Mais ça a l'air si artificiel @James
asyncwait
1
Je pense que c'est la meilleure réponse car elle garde le même nom. Similaire à la façon dont d'autres langues courantes le font.
eonist
1
@RiverSatya Pourquoi ne pas simplement utiliser le paramètre directement?
Declan McKenna
1
Vraiment une suggestion géniale. Nous allons l'implémenter de cette façon dans Swiftify :)
Crulex
13

Beaucoup de gens suggèrent un inoutparamètre, mais ce n'est vraiment pas pour cela qu'ils sont conçus. De plus, il ne permet pas d'appeler la fonction avec une letconstante, ni avec une chaîne littérale. Pourquoi n'ajoutez-vous pas simplement la valeur par défaut à la signature de la fonction?

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

Assurez-vous simplement de ne pas appeler getQuestionListavec la chaîne vide au cas où vous voudriez la langue par défaut, mais laissez simplement le paramètre de côté:

let list = getQuestionList() // uses the default "NL" language
Tim Vermeulen
la source
3
Je ne comprends pas non plus pourquoi tout le monde a sauté sur la solution inout alors qu'OP ne l'utilisait même pas au début ...
Eric Aya
1
Ils supposaient que var et inout faisaient la même chose.
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)

Dans certains cas, comme je l'ai expérimenté (avec des configurations plus complexes impliquant des tableaux), la création d'une nouvelle propriété dans la méthode et la mutation de cette propriété peuvent ne pas toujours fonctionner. Sans oublier que vous encombrez la méthode au lieu de simplement l'ajouter inoutà un paramètre et &à son argument, ce pour quoi cette syntaxe a été créée.

bsod
la source
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
la source
0

Je pense que les réponses @Harris et @garanda sont la meilleure approche.

Quoi qu'il en soit dans votre cas, il n'y a pas besoin de var, vous pouvez faire:

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
la source
0

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

Paramètres d'entrée-sortie

Les paramètres de fonction sont des constantes par défaut. Tenter de modifier la valeur d'un paramètre de fonction à partir du corps de cette fonction entraîne une erreur de compilation. Cela signifie que vous ne pouvez pas modifier la valeur d'un paramètre par erreur. Si vous souhaitez qu'une fonction modifie la valeur d'un paramètre et que vous souhaitez que ces modifications persistent après la fin de l'appel de fonction, définissez ce paramètre comme paramètre d'entrée-sortie à la place.

Vous écrivez un paramètre in-out en plaçant le mot clé inout juste avant le type d'un paramètre. Un paramètre d'entrée-sortie a une valeur qui est transmise à la fonction, est modifiée par la fonction et est renvoyée hors de la fonction pour remplacer la valeur d'origine. Pour une discussion détaillée du comportement des paramètres d'entrée-sortie et des optimisations associées du compilateur, consultez Paramètres d'entrée-sortie.

Vous ne pouvez passer une variable que comme argument d'un paramètre d'entrée-sortie. Vous ne pouvez pas passer une constante ou une valeur littérale comme argument, car les constantes et les littéraux ne peuvent pas être modifiés. Vous placez une esperluette (&) directement avant le nom d'une variable lorsque vous la transmettez comme argument à un paramètre d'entrée-sortie, pour indiquer qu'elle peut être modifiée par la fonction.

REMARQUE

Les paramètres in-out ne peuvent pas avoir de valeurs par défaut et les paramètres variadiques ne peuvent pas être marqués comme inout.

Voici un exemple de fonction appelée swapTwoInts ( : :), qui a deux paramètres entiers d'entrée-sortie appelés a et b:

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

La fonction swapTwoInts ( : :) permute simplement la valeur de b en a et la valeur de a en b. La fonction effectue cette permutation en stockant la valeur de a dans une constante temporaire appelée temporaireA, en affectant la valeur de b à a, puis en affectant temporaireA à b.

Vous pouvez appeler la fonction swapTwoInts ( : :) avec deux variables de type Int pour échanger leurs valeurs. Notez que les noms de someInt et anotherInt sont précédés d'une esperluette lorsqu'ils sont passés à la fonction swapTwoInts ( : :):

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"

L'exemple ci-dessus montre que les valeurs d'origine de someInt et anotherInt sont modifiées par la fonction swapTwoInts ( : :), même si elles ont été définies à l'origine en dehors de la fonction.

REMARQUE

Les paramètres d'entrée-sortie ne sont pas les mêmes que le renvoi d'une valeur à partir d'une fonction. L'exemple swapTwoInts ci-dessus ne définit pas de type de retour ou ne renvoie pas de valeur, mais il modifie toujours les valeurs de someInt et anotherInt. Les paramètres d'entrée-sortie sont une autre façon pour une fonction d'avoir un effet en dehors de la portée de son corps de fonction.

Mustafa Mohammed
la source
0

Voici une autre idée. Mon cas d'utilisation était de passer autour d'un tableau de chaînes pour y ajouter, pour lequel le tableau doit être passé de manière mutuelle. Je ne voulais pas non plus avoir d'état dans ma classe pour cela. J'ai donc créé une classe qui contient le tableau et je la transmets. En fonction de votre cas d'utilisation, il peut sembler idiot d'avoir une classe qui ne contient qu'une seule variable.

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

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

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

Je n'utilise appendque des joinedméthodes et sur le tableau, il était donc facile de changer le type avec un minimum d'autres changements dans mon code.

Quelques exemples d'utilisation:

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
la source
Ma réponse est si vous VOULEZ que la valeur d'origine vue par l'appelant soit modifiée. Si vous vouliez simplement pouvoir réattribuer localement la valeur sans que cela affecte l'appelant, les autres réponses ci-dessus traitent de cela.
webjprgm