Swift: Wache lass gegen wenn lass

129

Ich habe in Swift über Optionals gelesen und Beispiele gesehen, in denen if letüberprüft wird, ob ein Optional einen Wert enthält, und falls dies der Fall ist, etwas mit dem nicht umschlossenen Wert zu tun.

Ich habe jedoch gesehen, dass in Swift 2.0 das Schlüsselwort guard letmeistens verwendet wird. Ich frage mich, ob if letes aus Swift 2.0 entfernt wurde oder ob es noch verwendet werden kann.

Soll ich meine Programme ändern, enthalten if letzu guard let?

lmiguelvargasf
quelle

Antworten:

163

if letund guard letdienen ähnlichen, aber unterschiedlichen Zwecken.

Der "else" -Fall von guardmuss den aktuellen Bereich verlassen. Im Allgemeinen bedeutet dies return, dass das Programm aufgerufen oder abgebrochen werden muss . guardwird verwendet, um eine frühzeitige Rückkehr zu ermöglichen, ohne dass der Rest der Funktion verschachtelt werden muss.

if letverschachtelt seinen Umfang und erfordert nichts Besonderes davon. Es kann returnoder nicht.

Wenn der if-letBlock der Rest der Funktion sein sollte oder seine elseKlausel ein returnoder einen Abbruch enthalten würde, sollten Sie im Allgemeinen guardstattdessen verwenden. Dies bedeutet oft (zumindest nach meiner Erfahrung), dass im Zweifelsfall guardnormalerweise die bessere Antwort ist. Aber es gibt viele Situationen, in denen dies if letnoch angemessen ist.

Rob Napier
quelle
37
Verwenden Sie diese Option, if letwenn der non-nilFall gültig ist. Verwenden Sie diese Option, guardwenn der nilFall einen Fehler darstellt.
BallpointBen
4
@BallpointBen Dem stimme ich nicht zu. Es gibt viele Fälle, in denen dies guardangemessen ist, auch wenn kein Fehler vorliegt. Manchmal bedeutet es nur, dass nichts zu tun ist. Zum Beispiel positionTitlekönnte eine Methode guard if let title = title else {return}. Der Titel kann optional sein. In diesem Fall ist dies kein Fehler. Ist guard letaber trotzdem angebracht.
Rob Napier
1
Ja; Ich meinte Wache ließ den Kommentar ein.
Rob Napier
1
Mit anderen Worten, "guard let" wird verwendet, wenn der Code zu 99% sicher ist, dass die else-Bedingung nicht verwendet wird. in der anderen Hand "if let", wenn der Code 50 - 50 (Beispiel) ist, um die else-Bedingung zu verwenden.
Chino Pan
1
Die durch gebundene Variable if letist nur innerhalb des if let Gültigkeitsbereichs sichtbar . Die durch gebundene Variable guard letist danach sichtbar. Daher ist es sinnvoll, Guard zu verwenden, um auch optionale Werte zu binden.
Boweidmann
103

Wache kann Klarheit verbessern

Wenn Sie Guard verwenden, haben Sie eine viel höhere Erwartung, dass der Guard erfolgreich ist, und es ist etwas wichtig, dass Sie den Bereich nur vorzeitig verlassen möchten, wenn er nicht erfolgreich ist . Wie Sie schützen, um zu sehen, ob eine Datei / ein Bild vorhanden ist, ob ein Array leer ist oder nicht.

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
}

Wenn Sie den obigen Code mit if-let schreiben, wird dem lesenden Entwickler mitgeteilt, dass es sich eher um eine 50-50 handelt. Aber wenn Sie Guard verwenden, fügen Sie Ihrem Code Klarheit hinzu , und dies impliziert, dass ich davon ausgehe, dass dies in 95% der Fälle funktioniert. Sollte dies jemals fehlschlagen, weiß ich nicht, warum dies der Fall ist. Es ist sehr unwahrscheinlich ... aber verwenden Sie stattdessen einfach dieses Standardbild oder bestätigen Sie mit einer aussagekräftigen Nachricht, was schief gelaufen ist!

  • Vermeiden Sie guards, wenn sie Nebenwirkungen verursachen, Schutzvorrichtungen sind als natürlicher Fluss zu verwenden. Vermeiden Sie Wachen, wenn elseKlauseln Nebenwirkungen hervorrufen. Die Wachen legen die erforderlichen Bedingungen für die ordnungsgemäße Ausführung des Codes fest und bieten einen vorzeitigen Ausstieg

  • Wenn Sie im positiven Zweig eine signifikante Berechnung durchführen, refaktorieren Sie von ifzu einer guardAnweisung und geben den Fallback-Wert in der elseKlausel zurück

Aus: Erica Saduns Swift Style Buch

Aufgrund der obigen Vorschläge und des sauberen Codes ist es auch wahrscheinlicher, dass Sie Behauptungen zu fehlgeschlagenen Guard-Anweisungen hinzufügen möchten / müssen. Dies verbessert lediglich die Lesbarkeit und macht anderen Entwicklern klar, was Sie erwartet haben.

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

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

Aus: Erica Saduns Swift Style Buch + einige Modifikationen

(Sie werden keine Asserts / Voraussetzungen für if-lets verwenden. Es scheint einfach nicht richtig zu sein)

Die Verwendung von Wachen hilft Ihnen auch dabei, die Klarheit zu verbessern, indem Sie die Pyramide des Untergangs vermeiden . Siehe Nitins Antwort .


Guard erstellt eine neue Variable

Es gibt einen wichtigen Unterschied, den meines Erachtens niemand gut erklärt hat.

Beides guard letund if let entpacken Sie die Variable jedoch

Mit guard letSie schaffen eine neue Variable, wird existieren außerhalb der elseAussage.

Mit if letIhnen nicht zu schaffen sind , jeden neuer variabler nach der else - Anweisung, nur geben Sie den Code - Block , wenn die optionalen nicht-Null ist. Die neu erstellte Variable existiert nur innerhalb des Codeblocks, nicht danach!

guard let:

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)
    }
}

Weitere Informationen finden if letSie unter: Warum die erneute Deklaration der optionalen Bindung keinen Fehler verursacht


Guard erfordert das Verlassen des Oszilloskops

(Auch in Rob Napiers Antwort erwähnt):

Sie müssen innerhalb einer Funktion guarddefiniert haben . Der Hauptzweck besteht darin, den Bereich abzubrechen / zurückzugeben / zu beenden, wenn eine Bedingung nicht erfüllt ist:

var str : String?

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

Denn if letSie müssen es nicht in irgendeiner Funktion haben:

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

guard vs. if

Es ist erwähnenswert, dass es angemessener ist, diese Frage als guard letvs if letund guardvs zu betrachten if.

Ein Standalone ifführt kein Auspacken durch, ein Standalone auch nicht guard. Siehe Beispiel unten. Es wird nicht vorzeitig beendet, wenn ein Wert ist nil. Es gibt KEINE optionalen Werte. Es wird nur vorzeitig beendet, wenn eine Bedingung nicht erfüllt ist.

let array = ["a", "b", "c"]
func subscript(at index: Int) -> String?{
   guard index > 0, index < array.count  else { return nil} // exit early with bad index
   return array[index]
}
Honig
quelle
45

Wann if-letund wann zu verwenden guardist oft eine Frage des Stils.

Angenommen, Sie haben func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intein optionales Array von Elementen ( var optionalArray: [SomeType]?), und Sie müssen entweder zurückgeben, 0wenn das Array nil(nicht festgelegt) ist oder countwenn das Array einen Wert hat (festgelegt ist).

Sie können es folgendermaßen implementieren if-let:

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

oder so mit guard:

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

Die Beispiele sind funktional identisch.

Wo guard wirklich glänzt, ist, wenn Sie eine Aufgabe wie das Überprüfen von Daten haben und möchten, dass die Funktion frühzeitig fehlschlägt, wenn etwas nicht stimmt.

Anstatt eine Reihe von if-lets zu verschachteln, wenn Sie sich dem Abschluss der Validierung nähern, befinden sich der "Erfolgspfad" und die jetzt erfolgreich gebundenen Optionen im Hauptbereich der Methode, da alle Fehlerpfade bereits zurückgegeben wurden.

divergio
quelle
29

Ich werde versuchen, die Nützlichkeit von Guard-Anweisungen mit einem (nicht optimierten) Code zu erklären.

Sie haben eine Benutzeroberfläche, in der Sie Textfelder für die Benutzerregistrierung mit Vorname, Nachname, E-Mail, Telefon und Passwort validieren.

Wenn ein textField keinen gültigen Text enthält, sollte dieses Feld firstResponder sein.

Hier ist der nicht optimierte Code:

//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()
    }
}

Sie können oben sehen, dass auf alle Zeichenfolgen (firstNameString, lastNameString usw.) nur im Rahmen der if-Anweisung zugegriffen werden kann. Daher wird diese "Pyramide des Untergangs" erstellt und es gibt viele Probleme, einschließlich der Lesbarkeit und der Leichtigkeit, Dinge zu verschieben (wenn die Reihenfolge der Felder geändert wird, müssen Sie den größten Teil dieses Codes neu schreiben).

Mit der Guard-Anweisung (im folgenden Code) können Sie sehen, dass diese Zeichenfolgen außerhalb von verfügbar sind {}und verwendet werden, wenn alle Felder gültig sind.

// 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)
}

Wenn sich die Reihenfolge der Felder ändert, verschieben Sie einfach die entsprechenden Codezeilen nach oben oder unten, und schon kann es losgehen.

Dies ist eine sehr einfache Erklärung und ein Anwendungsfall. Hoffe das hilft!

Nitin Alabur
quelle
13

Grundlegender Unterschied

Wache lassen

  1. Früh existieren Prozess aus dem Umfang
  2. Benötige Punktzahl wie Return, Throw usw.
  3. Erstellen Sie eine neue Variable, auf die außerhalb des Bereichs zugegriffen werden kann.

wenn lassen

  1. Kann nicht auf den Bereich zugreifen.
  2. Keine Notwendigkeit, eine Anweisung zurückzugeben. Aber wir können schreiben

HINWEIS: Beide werden zum Entpacken der optionalen Variablen verwendet.

Kiran K.
quelle
2

Die klarste Erklärung, die ich gesehen habe, war im Github Swift Style Guide :

if fügt eine Ebene der Tiefe hinzu:

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

guard nicht:

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

bewachen

  • Eine guardAnweisung wird verwendet, um die Programmsteuerung aus einem Bereich zu übertragen, wenn eine oder mehrere Bedingungen nicht erfüllt sind.

  • Der Wert einer Bedingung in einer guardAnweisung muss vom Typ Bool oder einem Typ sein, mit dem eine Überbrückung verbunden ist Bool. Die Bedingung kann auch eine optionale verbindliche Erklärung sein.

Eine Wachaussage hat folgende Form:

guard condition else {
    //Generally return
}

wenn lassen

  • Auch als optionale Bindung beliebt .
  • Für den Zugriff auf optionale Objekte verwenden wir if let.
if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}
Akshay
quelle
1

Ich habe das von Swift mit Bob gelernt.

Typisches 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")
          }
     }

Probleme mit Else-If

  1. Verschachtelte Klammern
  2. Müssen Sie jede Zeile lesen, um die Fehlermeldung zu erkennen

Guard-Anweisung Ein Guard-Block wird nur ausgeführt, wenn die Bedingung falsch ist, und verlässt die Funktion durch Rückgabe. Wenn die Bedingung erfüllt ist, ignoriert Swift den Schutzblock. Es bietet einen frühen Ausstieg und weniger Klammern. +

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.
       }

Packen Sie die Optionen mit Else-If aus

Eine Guard-Anweisung ist nicht nur nützlich, um einen typischen bedingten Block durch eine else-if-Anweisung zu ersetzen, sondern auch, um Optionen durch Minimieren der Anzahl der Klammern zu entpacken. Beginnen wir zum Vergleich damit, wie Sie mehrere Optionen mit else-if auspacken. Lassen Sie uns zunächst drei Optionen erstellen, die ausgepackt werden.

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

Der schlimmste Albtraum

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")
                         }
                  }

Der obige Code funktioniert zwar, verstößt aber gegen das DRY-Prinzip. Es ist grausam. Lassen Sie es uns zusammenfassen. +

Etwas besser Der folgende Code ist besser lesbar als oben. +

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
                           }
     }

Mit Guard auspacken Die else-if-Anweisungen können durch Guard ersetzt werden. +

 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)
         }

Mehrere Optionen mit Else-If auspacken Bisher haben Sie die Optionen einzeln ausgepackt . Mit Swift können wir mehrere Optionen gleichzeitig auspacken. Wenn einer von ihnen nil enthält, wird der else-Block ausgeführt.

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")
  }
}

Beachten Sie, dass Sie beim gleichzeitigen Auspacken mehrerer Optionen nicht erkennen können, welche Option Null enthält

Mehrere Optionen mit Guard auspacken Natürlich sollten wir Guard over else-if verwenden. +

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
}
Ourang-Zeb Khan
quelle
Bitte gehen Sie zurück und korrigieren Sie Ihre Code-Formatierung / Einrückung!
pkamb