Swift 2 a introduit le mot-clé guard
, qui pourrait être utilisé pour s'assurer que diverses données sont prêtes à être configurées. Un exemple que j'ai vu sur ce site Web illustre une fonction submitTapped:
func submitTapped() {
guard username.text.characters.count > 0 else {
return
}
print("All good")
}
Je me demande si l'utilisation de guard
diffère de la manière habituelle, en utilisant une condition if
. Donne-t-il des avantages que vous ne pourriez pas obtenir en utilisant un simple chèque?
Lecture cet article J'ai constaté de grands avantages avec Guard
Ici, vous pouvez comparer l’utilisation de guard avec un exemple:
C'est la partie sans garde:
func fooBinding(x: Int?) {
if let x = x where x > 0 {
// Do stuff with x
x.description
}
// Value requirements not met, do something
}
Ici, vous mettez le code désiré dans toutes les conditions
Cela ne vous pose peut-être pas immédiatement un problème, mais vous pouvez imaginer à quel point cela pourrait devenir confus s'il était imbriqué dans de nombreuses conditions qui devaient toutes être remplies avant d'exécuter vos déclarations.
La meilleure façon de résoudre ce problème consiste à effectuer chacune de vos vérifications en premier, puis à quitter si aucune n’est remplie. Cela permet de comprendre facilement quelles conditions feront quitter cette fonction.
Mais maintenant, nous pouvons utiliser la garde et nous pouvons voir qu'il est possible de résoudre certains problèmes:
func fooGuard(x: Int?) {
guard let x = x where x > 0 else {
// Value requirements not met, do something
return
}
// Do stuff with x
x.description
}
- Vérifier la condition que vous voulez, pas celle que vous ne voulez pas. Ceci est similaire à une affirmation. Si la condition n’est pas remplie, l’instruction else de guard est exécutée et sort de la fonction.
- Si la condition est satisfaite, la variable facultative est décompressée automatiquement pour vous dans la portée de l'appel de l'instruction Guard - dans ce cas, la fonction fooGuard (_ :).
- Vous vérifiez les mauvaises affaires à un stade précoce, ce qui rend votre fonction plus lisible et plus facile à gérer.
Ce même schéma s'applique également aux valeurs non facultatives:
func fooNonOptionalGood(x: Int) {
guard x > 0 else {
// Value requirements not met, do something
return
}
// Do stuff with x
}
func fooNonOptionalBad(x: Int) {
if x <= 0 {
// Value requirements not met, do something
return
}
// Do stuff with x
}
Si vous avez encore des questions, vous pouvez lire l’intégralité de l’article: Déclaration de garde rapide.
Conclusion
Et enfin, en lisant et en testant, j’ai trouvé que si vous utilisiez la garde pour déballer les options,
ces valeurs non enveloppées restent pour que vous les utilisiez dans le reste de votre bloc de code
.
guard let unwrappedName = userName else {
return
}
print("Your username is \(unwrappedName)")
Ici, la valeur non emballée ne serait disponible que dans le bloc if
if let unwrappedName = userName {
print("Your username is \(unwrappedName)")
} else {
return
}
// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")
Contrairement à if
, guard
crée la variable accessible de l'extérieur du bloc. Il est utile de déballer beaucoup de Optional
s.
Il y a vraiment deux gros avantages à guard
. L’une consiste à éviter la pyramide de Doom, comme d’autres l'ont déjà mentionné - de nombreuses déclarations if let
agaçantes imbriquées l'une dans l'autre se déplaçant de plus en plus à droite.
L’autre avantage est souvent que la logique que vous souhaitez implémenter est plus "if not let
" que "if let { } else
".
Voici un exemple: supposons que vous souhaitiez implémenter accumulate
- un croisement entre map
et reduce
où il vous restitue un tableau de en cours d’exécution. La voici avec guard
:
extension Sliceable where SubSlice.Generator.Element == Generator.Element {
func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
// if there are no elements, I just want to bail out and
// return an empty array
guard var running = self.first else { return [] }
// running will now be an unwrapped non-optional
var result = [running]
// dropFirst is safe because the collection
// must have at least one element at this point
for x in dropFirst(self) {
running = combine(running, x)
result.append(running)
}
return result
}
}
let a = [1,2,3].accumulate(+) // [1,3,6]
let b = [Int]().accumulate(+) // []
Comment l'écririez-vous sans garde, mais en utilisant toujours first
qui renvoie une valeur facultative? Quelque chose comme ça:
extension Sliceable where SubSlice.Generator.Element == Generator.Element {
func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
if var running = self.first {
var result = [running]
for x in dropFirst(self) {
running = combine(running, x)
result.append(running)
}
return result
}
else {
return []
}
}
}
L’imbrication supplémentaire est gênante, mais il n’est pas aussi logique d’avoir la if
et la else
aussi éloignées les unes des autres. C’est beaucoup plus lisible d’avoir la sortie anticipée pour le cas vide, puis de continuer avec le reste de la fonction comme si cela n’était pas possible.
Lorsqu'une condition est remplie avec guard
, elle expose les variables déclarées dans le bloc guard
au reste du bloc de code, en les mettant dans son champ d'application. Comme indiqué précédemment, ce qui sera certainement utile avec les instructions imbriquées if let
.
Notez que la garde nécessite un retour ou un lancer dans sa déclaration else.
Vous trouverez ci-dessous un exemple d'analyse syntaxique d'un objet JSON à l'aide de guard plutôt que de if-let. Ceci est un extrait d'une entrée de blog qui inclut un fichier de terrain de jeu que vous pouvez trouver ici:
Comment utiliser Guard dans Swift 2 pour analyser JSON
func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {
guard let firstname = data["First"] as? String else {
return Developer() // we could return a nil Developer()
}
guard let lastname = data["Last"] as? String else {
throw ParseError.BadName // or we could throw a custom exception and handle the error
}
guard let website = data["WebSite"] as? String else {
throw ParseError.BadName
}
guard let iosDev = data["iosDeveloper"] as? Bool else {
throw ParseError.BadName
}
return Developer(first: firstname, last: lastname, site: website, ios: iosDev)
}
télécharger l'aire de jeu: aire de jeu de la garde
Voici un extrait du Le Swift Guide du langage de programmation:
Si la condition de l'instruction Guard est remplie, l'exécution du code se poursuit après l'accolade fermante de l'instruction Guard. Toutes les variables ou constantes auxquelles des valeurs ont été affectées à l'aide d'une liaison facultative en tant que partie de la condition sont disponibles pour le reste du bloc de code dans lequel l'instruction Guard apparaît.
Si cette condition n'est pas remplie, le code à l'intérieur de la branche else est exécuté. Cette branche doit transférer le contrôle pour quitter le bloc de code dans lequel cette instruction de protection apparaît. Elle peut le faire avec une instruction de transfert de contrôle telle que return, break ou continue, ou elle peut appeler une fonction ou une méthode qui ne renvoie pas, telle que comme fatalError ().
L'un des avantages est l'élimination de nombreuses instructions if let
imbriquées. Voir la vidéo "Quoi de neuf dans Swift" de la WWDC aux alentours de 15h30, dans la section intitulée "Pyramid of Doom".
Déclaration de garde va faire. c'est un couple de différent
1) il me permet de réduire imbriqué si déclaration
2) c’est augmenter ma portée que ma variable accessible
si déclaration
func doTatal(num1 : Int?, num2: Int?) {
// nested if statement
if let fistNum = num1 where num1 > 0 {
if let lastNum = num2 where num2 < 50 {
let total = fistNum + lastNum
}
}
// don't allow me to access out of the scope
//total = fistNum + lastNum
}
déclaration de garde
func doTatal(num1 : Int?, num2: Int?) {
//reduce nested if statement and check positive way not negative way
guard let fistNum = num1 where num1 > 0 else{
return
}
guard let lastNum = num2 where num2 < 50 else {
return
}
// increase my scope which my variable accessible
let total = fistNum + lastNum
}
Si vous avez un contrôleur de vue avec quelques éléments UITextField ou un autre type d’entrée utilisateur, vous remarquerez immédiatement que vous devez déballer textField.text en option pour obtenir le texte à l’intérieur (le cas échéant!). isEmpty ne vous fera aucun bien ici, sans aucune entrée, le champ de texte retournera simplement nil.
Vous en avez donc quelques-uns que vous décompressez et que vous transmettez éventuellement à une fonction qui les publie sur un noeud final de serveur. Nous ne souhaitons pas que le code de serveur traite des valeurs nulles ou envoie par erreur des valeurs non valides au serveur. Nous allons donc déballer ces valeurs d'entrée avec guard.
func submit() {
guard let name = nameField.text else {
show("No name to submit")
return
}
guard let address = addressField.text else {
show("No address to submit")
return
}
guard let phone = phoneField.text else {
show("No phone to submit")
return
}
sendToServer(name, address: address, phone: phone)
}
func sendToServer(name: String, address: String, phone: String) {
...
}
Vous remarquerez que notre fonction de communication avec le serveur prend des valeurs de chaîne non facultatives en tant que paramètres, d’où le déploiement préalable de la protection. Le décompactage est un peu peu intuitif car nous sommes habitués à décompresser avec if, qui permet de décompresser les valeurs à utiliser dans un bloc. Ici, la déclaration de garde a un bloc associé mais c’est en réalité un bloc d’autre - c’est-à-dire ce que vous faites si le décompression échoue - les valeurs sont décompressées directement dans le même contexte que la déclaration elle-même.
// séparation des préoccupations
Sans garde, nous nous retrouverions avec un gros tas de code qui ressemblerait à une pyramide de Doom . Cela ne convient pas à l’ajout de nouveaux champs à notre formulaire ni à un code très lisible. Il peut être difficile de suivre l’indentation, en particulier avec tant d’autres déclarations à chaque branche.
func nonguardSubmit() {
if let name = nameField.text {
if let address = addressField.text {
if let phone = phoneField.text {
sendToServer(name, address: address, phone: phone)
} else {
show("no phone to submit")
}
} else {
show("no address to submit")
}
} else {
show("no name to submit")
}
}
Oui, nous pourrions même combiner toutes ces instructions if si let en une seule instruction séparée par des virgules, mais nous perdrions la possibilité de déterminer laquelle avait échoué et de présenter un message à l'utilisateur.
En utilisant garde notre intension est claire. nous ne voulons pas exécuter le reste du code si cette condition particulière n'est pas remplie. Ici, nous pouvons aussi rallonger la chaîne, veuillez regarder le code ci-dessous:
guard let value1 = number1, let value2 = number2 else { return }
// do stuff here
De la documentation Apple:
Déclaration de garde
Une instruction guard est utilisée pour transférer le contrôle de programme hors d’une portée si une ou plusieurs conditions ne sont pas remplies.
Synatx:
guard condition else {
statements
}
Avantage:
1. En utilisant l'instruction guard
, nous pouvons nous débarrasser des conditionnels profondément imbriqués dont le seul but est de valider un ensemble d'exigences.
2. Il a été conçu spécifiquement pour quitter une méthode ou une fonction tôt.
si vous utilisez si let ci-dessous est le code à quoi il ressemble.
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error == nil {
if let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 {
if let data = data {
//Process Data Here.
print("Data: \(data)")
} else {
print("No data was returned by the request!")
}
} else {
print("Your request returned a status code other than 2XX!")
}
} else {
print("Error Info: \(error.debugDescription)")
}
}
task.resume()
À l'aide de guard, vous pouvez transférer le contrôle hors du domaine d'application si une ou plusieurs conditions ne sont pas remplies.
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
/* GUARD: was there an error? */
guard (error == nil) else {
print("There was an error with your request: \(error)")
return
}
/* GUARD: Did we get a successful 2XX response? */
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
print("Your request returned a status code other than 2XX!")
return
}
/* GUARD: was there any data returned? */
guard let data = data else {
print("No data was returned by the request!")
return
}
//Process Data Here.
print("Data: \(data)")
}
task.resume()
Référence:
1. Swift 2: Quitter tôt avec garde 2. dacity 3. Déclaration de la garde
Comme une instruction if, guard exécute des instructions en fonction de la valeur booléenne d'une expression. Contrairement à une instruction if, les instructions de garde ne sont exécutées que si les conditions ne sont pas remplies. Vous pouvez penser à la garde plutôt à une assertion, mais plutôt que de vous écraser, vous pouvez quitter avec élégance.
se référer à: http://ericcerney.com/Swift-guard-statement/
En termes simples, il fournit un moyen de valider les champs avant leur exécution. C'est un bon style de programmation car il améliore la lisibilité. Dans d'autres langues, cela peut ressembler à ceci:
func doSomething() {
if something == nil {
// return, break, throw error, etc.
}
...
}
Mais parce que Swift vous fournit des options, nous ne pouvons pas vérifier si c'est nul et affecter sa valeur à une variable. En revanche, if let
vérifie que c'est pas nil et affecte une variable pour conserver la valeur réelle. C'est ici que guard
entre en jeu. Cela vous donne un moyen plus concis de sortir plus tôt en utilisant des options.
Cela rend vraiment le flux d'une séquence avec plusieurs recherches et options optionnelles beaucoup plus concis et clair et réduit considérablement les possibilités d'imbrication. Voir le post d'Erica Sadun pour remplacer Ifs . .... Pourrait s'emporter, un exemple ci-dessous:
let filteredLinks = locationsLinkedToList.filter({$0.actionVerb == movementCommand})
guard let foundLink = filteredLinks.first else {return ("<Person> cannot go in that direction.", nil, nil)}
guard filteredLinks.count == 1 else {return ("<Person> cannot decide which route to take.", nil, nil)}
guard let nextLocation = foundLink.toLocation else {return ("<Person> cannot go in that direction.", nil, nil)}
Voyez si ça colle.
Source: https://thenucleargeeks.com/2019/05/09/guard-in-Swift/
Voyons l'exemple pour bien le comprendre
Exemple 1:
func validate() {
guard 3>2 else {
print ("False")
return
}
print ("True") //True
}
validate()
Dans l'exemple ci-dessus, nous voyons que 3 est supérieur à 2 et que l'instruction à l'intérieur de la clause guard else est ignorée et que True est imprimé.
Exemple 2:
func validate() {
guard 1>2 else {
print ("False") //False
return
}
print ("True")
}
validate()
Dans l'exemple ci-dessus, nous voyons que 1 est inférieur à 2 et que l'instruction à l'intérieur de la clause guard else est exécutée et que False est imprimé suivi de return.
Example 3: gaurd let, unwrapping optionals through guard let
func getName(args myName: String?) {
guard let name = myName, !name.isEmpty else {
print ("Condition is false") // Condition is false return
}
print("Condition is met\(name)")
}
getName(args: "")
Dans l'exemple ci-dessus, nous utilisons un garde qui permet de déballer les options. Dans la fonction getName, nous avons défini une variable de type chaîne myName qui est facultative. Nous utilisons ensuite guard let pour vérifier si la variable myName est nulle ou non, si non assigner à name et vérifier à nouveau, name n'est pas vide. Si les deux conditions sont qualifiées, c'est-à-dire true, le bloc else est ignoré et affiche "Les conditions sont remplies avec nom".
Fondamentalement, nous vérifions ici deux choses séparées par une virgule, d’abord dépliante et facultative, et nous vérifions si cela satisfait ou non la condition.
Ici, nous ne transmettons rien à la fonction, c’est-à-dire une chaîne vide. La condition est false est print.
func getName(args myName: String?) {
guard let name = myName, !name.isEmpty else {
print ("Condition is false")
return
}
print("Condition is met \(name)") // Condition is met Hello
} getName(args: "Hello")
Ici, nous passons "Hello" à la fonction et vous pouvez voir que le résultat est imprimé "La condition est remplie, Hello".