web-dev-qa-db-fra.com

Swift: garde vs si laisse

Je lisais des articles sur Optionals dans Swift, et j’ai vu des exemples où if let est utilisé pour vérifier si un facultatif contient une valeur et, le cas échéant, effectue quelque chose avec la valeur non enveloppée.

Cependant, j'ai vu que dans Swift 2.0, le mot clé guard est principalement utilisé. Je me demande si if let a été supprimé de Swift 2.0 ou s’il est encore possible de l’utiliser.

Devrais-je changer mes programmes contenant if let à guard?

98
lmiguelvargasf

if let et guard let servent des buts similaires, mais distincts.

Le cas "else" de guard doit quitter la portée actuelle. Généralement, cela signifie qu’il doit appeler return ou abandonner le programme. guard est utilisé pour fournir un retour rapide sans nécessiter d'imbrication du reste de la fonction.

if let _ nids sa portée, et n'exige rien de spécial. Il peut return ou non.

En général, si le if-letbloque serait le reste de la fonction, ou sa clause else aurait un return ou serait abandonnée, vous devriez alors utiliser guard. Cela signifie souvent (du moins selon mon expérience) qu'en cas de doute, guard est généralement la meilleure réponse. Mais il y a beaucoup de situations où if let est toujours approprié.

136
Rob Napier

Garde peut améliorer clarté

Lorsque vous utilisez la garde, vous attendez beaucoup plus haut pour la garde réussir et il est assez important que si pas réussi, alors vous voulez juste quitter scope early. Comme si vous gardiez pour voir si un fichier/image existe, si un tableau est vide ou non.

func icon() -> UIImage {
    guard let image = UIImage(named: "Photo") else {
        return UIImage(named: "Default")! //This is your fallback
    }
    return image //-----------------you're always expecting/hoping this to happen
}

Si vous écrivez le code ci-dessus avec if-let, cela indique au développeur que c'est plus un 50-50. Mais si vous utilisez guard, vous ajoutez clarté à votre code et cela implique que cela fonctionne 95% du temps ... si cela échoue, je ne sais pas pourquoi; c'est très peu probable ... mais alors utilisez simplement cette image par défaut à la place ou peut-être simplement assert avec un message explicite décrivant ce qui s'est mal passé!

  • Evitez guard s quand ils créent des effets secondaires, les gardes doivent être utilisés comme un flux naturel . Évitez les gardes quand les clauses else introduisent des effets secondaires. Les gardes établissent les conditions requises pour que le code s'exécute correctement, offrant une sortie anticipée

  • Lorsque vous effectuez un calcul significatif dans la branche positive, refactorez if en une instruction guard et renvoie la valeur de repli dans la clause else

De: de Erica Sadun Swift Style book

De plus, à la suite des suggestions ci-dessus et du code épuré, c'est plus probable vous voudrez/aurez besoin d'ajouter des assertions dans échoué des instructions de protection, cela améliore simplement la lisibilité et indique clairement aux autres développeurs ce à quoi vous vous attendiez.

guard​ ​let​ image = ​UIImage​(named: selectedImageName) else { // YESSSSSS
     assertionFailure(​"Missing ​​\(​selectedImageName​)​​ asset"​) 
     return
} 

guard​ ​let​ image = ​UIImage​(named: selectedImageName) else { // NOOOOOOO
​     ​return 
}

De: d'Erica Sadun Swift Livre de styles + quelques modifications

(vous n'utiliserez pas d'assert/preconditions pour if-lets. Ça ne semble pas juste)

L'utilisation de gardes vous aide également à améliorer la clarté de en évitant la pyramide de Doom. Voir la réponse de Nitin .


Guard crée une variable nouveau

Il y a une différence importante que je crois que personne n'a bien expliqué.

guard et if let dérouler la variable cependant

Avec guard, vous créez une nouvelle variable qui existera en dehors de else déclaration.

Avec if let _ vous ne créez pas de nouvelle variable - après l'instruction else, vous ne faites que entrer le bloc de code = si l'option est non-nulle. La nouvelle variable créée n'existe que à l'intérieur du code bloqué pas après!

guard:

func someFunc(blog: String?) {

    guard let blogName = blog else {
        print("some ErrorMessage")
        print(blogName) // will create an error Because blogName isn't defined yet
        return
    }
    print(blogName) // You can access it here ie AFTER the guard statement!!

    //And if I decided to do 'another' guard let with the same name ie 'blogName' then I would create an error!
    guard let blogName = blog else { // errorLine: Definition Conflicts with previous value.
        print(" Some errorMessage")
        return
    }
    print(blogName)
}

if-let:

func someFunc(blog: String?) {


    if let blogName1 = blog {
        print(blogName1) // You can only access it inside the code block. Outside code block it doesn't exist!
    }
    if let blogName1 = blog { // No Error at this line! Because blogName only exists inside the code block ie {}
        print(blogName1)
    }
}

Pour plus d'informations sur if let voir: Pourquoi la redéclaration de liaison facultative ne crée pas d'erreur


Guard a besoin de scope out

(Également mentionné dans la réponse de Rob Napier):

Vous DEVEZ avoir guard défini à l'intérieur une fonction. Son but principal est d’avorter/retourner/sortir de la portée, si une condition n’est pas remplie:

var str : String?

guard let blogName1 = str else {
    print("some error")
    return // Error: Return invalid outside of a func
}
print (blogName1)

Pour if let vous n’avez pas besoin de l’avoir dans une fonction quelconque:

var str : String?    
if let blogName1 = str {
   print(blogName1) // You don't get any errors!
}
83
Honey

Quand utiliser if-let Et quand utiliser guard est souvent une question de style.

Supposons que vous avez func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int et un tableau optionnel d'éléments (var optionalArray: [SomeType]?), Et que vous devez renvoyer soit 0 Si le tableau est nil (non- set) ou count si le tableau a une valeur (est définie).

Vous pouvez l'implémenter comme ceci en utilisant if-let:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        if let array = optionalArray {
            return array.count
        }
        return 0
    }

ou comme ceci en utilisant guard:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        guard let array = optionalArray else {
            return 0
        }
        return array.count
    }

Les exemples sont fonctionnellement identiques.

guard brille vraiment, c’est lorsque vous avez une tâche comme la validation des données et que vous souhaitez que la fonction échoue rapidement si quelque chose ne va pas.

Au lieu d'imbriquer un groupe de if-let Lorsque vous vous approchez de la fin de la validation, le "chemin de réussite" et les options optionnelles désormais liées avec succès sont tous dans la portée principale de la méthode, car les chemins d'échec ont déjà été renvoyés. .

45
divergio

Je vais essayer d'expliquer l'utilité des déclarations de garde avec du code (non optimisé).

Vous avez une interface utilisateur dans laquelle vous validez les champs de texte pour l'enregistrement de l'utilisateur avec le nom, le nom, l'adresse email, le téléphone et le mot de passe.

Si un objet textField ne contient pas de texte valide, il convient que ce champ devienne firstResponder.

voici le code non optimisé:

//pyramid of Doom

func validateFieldsAndContinueRegistration() {
    if let firstNameString = firstName.text where firstNameString.characters.count > 0{
        if let lastNameString = lastName.text where lastNameString.characters.count > 0{
            if let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") {
                if let passwordString = password.text where passwordString.characters.count > 7{
                    // all text fields have valid text
                    let accountModel = AccountModel()
                    accountModel.firstName = firstNameString
                    accountModel.lastName = lastNameString
                    accountModel.email = emailString
                    accountModel.password = passwordString
                    APIHandler.sharedInstance.registerUser(accountModel)
                } else {
                    password.becomeFirstResponder()
                }
            } else {
                email.becomeFirstResponder()
            }
        } else {
            lastName.becomeFirstResponder()
        }
    } else {
        firstName.becomeFirstResponder()
    }
}

Vous pouvez voir ci-dessus que toutes les chaînes (firstNameString, lastNameString, etc.) ne sont accessibles que dans la portée de l'instruction if. il crée donc cette "pyramide de Doom" et pose de nombreux problèmes, notamment de lisibilité et de facilité de déplacement (si l'ordre des champs est modifié, vous devez réécrire l'essentiel de ce code).

Avec l'instruction guard (dans le code ci-dessous), vous pouvez voir que ces chaînes sont disponibles en dehors de la chaîne {} et sont utilisés si tous les champs sont valides.

// guard let no pyramid of Doom
func validateFieldsAndContinueRegistration() {

guard let firstNameString = firstName.text where firstNameString.characters.count > 0 else {
            firstName.becomeFirstResponder()
            return
        }
guard let lastNameString = lastName.text where lastNameString.characters.count > 0 else {
            lastName.becomeFirstResponder()
            return
        }
guard let emailString = email.text where 
        emailString.characters.count > 3 &&
        emailString.containsString("@") && 
        emailString.containsString(".") else {
            email.becomeFirstResponder()
            return
        }
guard let passwordString = password.text where passwordString.characters.count > 7 else {
            password.becomeFirstResponder()
            return
        }

// all text fields have valid text
    let accountModel = AccountModel()
    accountModel.firstName = firstNameString
    accountModel.lastName = lastNameString
    accountModel.email = emailString
    accountModel.password = passwordString
    APIHandler.sharedInstance.registerUser(accountModel)
}

Si l'ordre des champs change, déplacez simplement les lignes de code respectives vers le haut ou le bas et vous êtes prêt à partir.

Ceci est une explication très simple et un cas d'utilisation. J'espère que cela t'aides!

28
Nitin Alabur

Différence de base

Garde a laissé

  1. Early existe processus de la portée
  2. Exiger un score existant comme le retour, le lancer, etc.
  3. Créez une nouvelle variable qui peut être un accès hors de la portée.

si laisse

  1. Impossible d'accéder à la portée.
  2. pas besoin de retourner la déclaration. Mais on peut écrire

Remarque: les deux sont utilisés pour déballer la variable facultative.

8
Kiran K

L'explication la plus claire que j'ai vue se trouvait dans le Github Swift Style Guide) :

if ajoute un niveau de profondeur:

if n.isNumber {
    // Use n here
} else {
    return
}

guard ne:

guard n.isNumber else {
    return
}
// Use n here
2
wildpeaks

Garde

  • Une instruction de protection 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.

  • La valeur d'une condition dans une instruction guard doit être de type Bool ou d'un type ponté en Bool. La condition peut aussi être une déclaration obligatoire

Une déclaration de garde a la forme suivante:

guard condition else {
    //Generally return
}

si let

  • Aussi populaire que reliure optionnelle
  • Pour accéder à un objet optionnel, nous utilisons si let
if let roomCount = optionalValue {
       print("roomCount available")
    } else {
        print("roomCount is nil")
 }
2
Akshay

J'ai appris cela de Swift avec Bob ..

Typique Else-If

 func checkDrinkingAge() {
      let canDrink = true

     if canDrink {
        print("You may enter")
       // More Code
        // More Code
      // More Code

         } else {
         // More Code
    // More Code
    // More Code
    print("Let me take you to the jail")
          }
     }

Problèmes avec Else-If

  1. Crochets imbriqués
  2. Il faut lire chaque ligne pour repérer le message d'erreur

Guard Statement Un bloc de garde ne fonctionne que si la condition est fausse et il sortira de la fonction par le retour. Si la condition est vraie, Swift ignore le bloc de garde. Il fournit une sortie rapide et moins de crochets. +

func checkDrinkProgram() {
       let iCanDrink = true

           guard iCanDrink else {
        // if iCanDrink == false, run this block
         print("Let's me take you to the jail")
          return
        }

         print("You may drink")
           // You may move on
                  // Come on.
                 // You may leave
                // You don't need to read this.
                 // Only one bracket on the bottom: feeling zen.
       }

Déballez les options avec Else-If

Une instruction de protection est non seulement utile pour remplacer un bloc conditionnel typique par une instruction else-if, mais également très utile pour décompresser des options en minimisant le nombre de crochets. Pour comparer, commençons par comment décompresser plusieurs options avec else-if. Tout d’abord, créons trois options qui seront déballées.

var publicName: String? = "Bob Lee"
var publicPhoto: String? = "Bob's Face"
var publicAge: Int? = nil

Le pire cauchemar

func unwrapOneByOne() {
         if let name = publicName {
              if let photo = publicPhoto {
                     if let age = publicAge {
                        print("Bob: \(name), \(photo), \(age)")
                                  } else {
                          print("age is mising")
                           }
                  } else {
                      print("photo is missing")
                         }
                  } else {
                        print("name is missing")
                         }
                  }

Le code ci-dessus fonctionne certainement mais viole le principe DRY. C'est atroce. Décomposons-le. +

légèrement mieux Le code ci-dessous est plus lisible que ci-dessus. +

func unwrapBetter() {
         if let name = publicName {
       print("Yes name")
                   } else {
               print("No name")
        return
      }

         if let photo = publicPhoto {
             print("Yes photo")
            } else {
           print("No photo")
       return
             }

        if let age = publicAge {
            print("Yes age")
                      } else {
                print("No age")
            return
                           }
     }

Déballer avec Guard Les instructions else-if peuvent être remplacées par Guard. +

 func unwrapOneByOneWithGuard() {
             guard let name = publicName else {
                  print("Name missing")
              return
                                        }

              guard let photo = publicPhoto else {
              print("Photo missing")
                return
                                            }

                  guard let age = publicAge else {
                   print("Age missing")
                                     return
                                                 }
                 print(name)
                 print(photo)
                 print(age)
         }

Déballer plusieurs options avec Else-If Jusqu'à présent, vous avez décompressé les options une par une. Swift nous permet de décompresser plusieurs options en même temps. Si l'une d'elles contient la valeur nil, elle exécutera le bloc else.

func unwrap() {
  if let name = publicName, let photo = publicPhoto, let age = publicAge {
    print("Your name is \(name). I see your face right here, \(photo), you are \(age)")
  } else {
    // if any one of those is missing
    print("Something is missing")
  }
}

Sachez que lorsque vous déballez plusieurs options à la fois, vous ne pouvez pas identifier celles qui contiennent zéro

Déballez plusieurs options avec Guard Bien sûr, nous devrions utiliser la protection par-dessus-sinon. +

func unwrapWithGuard() {
  guard let name = publicName, let photo = publicPhoto, let age = publicAge else {
    // if one or two of the variables contain "nil"
    print("Something is missing")
    return
  }

  print("Your name is \(name). I see your, \(photo). You are \(age).")
  // Animation Logic
  // Networking
  // More Code, but still zen
}
0
Ourang-Zeb Khan