Swifts Schlüsselwort

197

Swift 2 führte das guardSchlüsselwort ein, mit dem sichergestellt werden kann, dass verschiedene Daten sofort einsatzbereit konfiguriert sind. Ein Beispiel, das ich auf dieser Website gesehen habe, zeigt eine submitTapped-Funktion:

func submitTapped() {
    guard username.text.characters.count > 0 else {
        return
    }

    print("All good")
}

Ich frage mich, ob die Verwendung guardanders ist als die altmodische Verwendung einer ifBedingung. Gibt es Vorteile, die Sie mit einem einfachen Scheck nicht erreichen könnten?

David Snabel
quelle
Siehe auch Wache gegen If-Let- Frage
Honey
Bitte beziehen Sie sich auf folgenden Link medium.com/@pvpriya7/swift-guard-18e59c50c624
Priyanka V

Antworten:

368

Beim Lesen dieses Artikels habe ich große Vorteile bei der Verwendung von Guard festgestellt

Hier können Sie die Verwendung von Guard mit einem Beispiel vergleichen:

Dies ist der Teil ohne Wache:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
  1. Hier setzen Sie Ihren gewünschten Code unter allen Bedingungen

    Möglicherweise sehen Sie nicht sofort ein Problem damit, aber Sie können sich vorstellen, wie verwirrend es werden könnte, wenn es mit zahlreichen Bedingungen verschachtelt ist, die alle erfüllt sein müssen, bevor Sie Ihre Anweisungen ausführen

Die Möglichkeit, dies zu bereinigen, besteht darin, zuerst jede Ihrer Überprüfungen durchzuführen und zu beenden, wenn keine erfüllt sind. Dies ermöglicht ein einfaches Verständnis der Bedingungen, unter denen diese Funktion beendet wird.

Aber jetzt können wir Guard verwenden und sehen, dass es möglich ist, einige Probleme zu lösen:

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
}
  1. Überprüfen Sie den Zustand, den Sie möchten, nicht den, den Sie nicht möchten. Dies ähnelt wiederum einer Behauptung. Wenn die Bedingung nicht erfüllt ist, wird die else-Anweisung von guard ausgeführt, die aus der Funktion ausbricht.
  2. Wenn die Bedingung erfüllt ist, wird die optionale Variable hier automatisch für Sie in dem Bereich entpackt, in dem die Guard-Anweisung aufgerufen wurde - in diesem Fall die Funktion fooGuard (_ :).
  3. Sie suchen frühzeitig nach schlimmen Fällen, um Ihre Funktion besser lesbar und leichter zu warten

Das gleiche Muster gilt auch für nicht optionale Werte:

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
}

Wenn Sie noch Fragen haben, können Sie den gesamten Artikel lesen: Swift Guard Statement.

Einpacken

Und schließlich stellte ich beim Lesen und Testen fest, dass, wenn Sie Guard verwenden, um alle Optionen auszupacken,

Diese nicht verpackten Werte bleiben für Sie im Rest Ihres Codeblocks verfügbar

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")

Hier wäre der entpackte Wert nur innerhalb des if-Blocks verfügbar

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)")
Jorge Casariego
quelle
3
Hey @Eric du hast einen exzellenten Beitrag gemacht! Vielen Dank, dass Sie es so einfach zu verstehen gemacht haben!
Jorge Casariego
1
Ich verwende Guard für das Auspacken von NSError. Wenn ich jedoch versuche, es im Schutzbereich zu verwenden (z. B. um einen Fehler an einen Rückruf zu übergeben), heißt es: "Die im Schutzzustand deklarierte Variable kann in ihrem Hauptteil nicht verwendet werden." Ist es sinnvoll? Danke
GeRyCh
6
Das Entpacken von @GeRyCh in eine Guard-Anweisung stellt diese Variable nach der Guard-Anweisung zur Verfügung, nicht innerhalb dieser. Ich habe eine Weile gebraucht, um mich daran zu gewöhnen.
DonnaLea
2
Hier ist ein weiterer ausgezeichneter Artikel über die Verwendung von Guard zum sauberen Auspacken von Optionen. Fasst es gut zusammen.
Doches
let x = x where x > 0Bedeutet das, dass Sie eine andere Bedingung in Ihre optionale Bindung eingekoppelt haben ? Ich meine, es ist ein bisschen anders alsif let constantName = someOptional { statements }
Honey
36

Im Gegensatz zu if, guardschafft die Variable, die von außerhalb des Blocks zugegriffen werden kann. Es ist nützlich, viele Optionals auszupacken .

takebayashi
quelle
24

Es gibt wirklich zwei große Vorteile guard. Einer vermeidet die Pyramide des Untergangs, wie andere bereits erwähnt haben - viele nervige if letAussagen, die ineinander verschachtelt sind, bewegen sich immer weiter nach rechts.

Der andere Vorteil ist oft, dass die Logik, die Sie implementieren möchten, mehr " if not let" als "ist.if let { } else " ist.

Hier ein Beispiel: Angenommen, Sie möchten implementieren accumulate- eine Kreuzung zwischen mapund reducewo es Ihnen ein Array von Laufreduzierungen zurückgibt . Hier ist es mit 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(+)  // []

Wie würden Sie es ohne Schutz schreiben , aber wenn Sie es trotzdem verwenden first, wird eine Option zurückgegeben? Etwas wie das:

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

}

Das zusätzliche Verschachteln ist ärgerlich, aber es ist auch nicht so logisch, das ifund das elseso weit voneinander entfernt zu haben. Es ist viel besser lesbar, den leeren Fall vorzeitig zu beenden und dann mit dem Rest der Funktion fortzufahren, als wäre dies keine Möglichkeit.

Fluggeschwindigkeit
quelle
19

Wenn eine Bedingung erfüllt ist, werden guardVariablen, die innerhalb des guardBlocks deklariert sind, dem Rest des Codeblocks ausgesetzt und in seinen Geltungsbereich gebracht. Was, wie bereits erwähnt, bei verschachtelten if letAnweisungen sicherlich nützlich sein wird .

Beachten Sie, dass der Guard eine Rückgabe oder einen Wurf in seiner else-Anweisung erfordert .

JSON mit Guard analysieren

Im Folgenden finden Sie ein Beispiel dafür, wie ein JSON-Objekt mithilfe von guard und nicht if-let analysiert werden kann. Dies ist ein Auszug aus einem Blogeintrag, der eine Spielplatzdatei enthält, die Sie hier finden:

Verwendung von Guard in Swift 2 zum Parsen von 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)

}

Spielplatz herunterladen: Wachspielplatz

Mehr Info:

Hier ist ein Auszug aus dem The Swift Programming Language Guide:

Wenn die Bedingung der Schutzanweisung erfüllt ist, wird die Codeausführung nach der schließenden Klammer der Schutzanweisung fortgesetzt. Alle Variablen oder Konstanten, denen mithilfe einer optionalen Bindung als Teil der Bedingung Werte zugewiesen wurden, sind für den Rest des Codeblocks verfügbar, in dem die Guard-Anweisung angezeigt wird.

Wenn diese Bedingung nicht erfüllt ist, wird der Code im Zweig else ausgeführt. Dieser Zweig muss die Steuerung übertragen, um den Codeblock zu verlassen, in dem diese Guard-Anweisung angezeigt wird. Er kann dies mit einer Steuerübertragungsanweisung wie return, break oder continue tun oder eine Funktion oder Methode aufrufen, die nicht zurückgibt, z als fatalError ().

Dan Beaulieu
quelle
7

Ein Vorteil ist die Beseitigung vieler verschachtelter if letAnweisungen. Sehen Sie sich das WWDC-Video "What's New in Swift" gegen 15:30 Uhr an, den Abschnitt mit dem Titel "Pyramid of Doom".

zaph
quelle
6

Wann man Wachen benutzt

Wenn Sie einen Ansichts-Controller mit einigen UITextField-Elementen oder einer anderen Art von Benutzereingabe haben, werden Sie sofort feststellen, dass Sie die optionale Datei textField.text auspacken müssen, um zum darin enthaltenen Text zu gelangen (falls vorhanden!). isEmpty bringt Ihnen hier nichts, ohne Eingabe gibt das Textfeld einfach null zurück.

Sie haben also einige davon, die Sie auspacken und schließlich an eine Funktion übergeben, die sie an einen Serverendpunkt sendet. Wir möchten nicht, dass der Servercode mit Nullwerten umgehen oder versehentlich ungültige Werte an den Server senden muss, damit wir diese Eingabewerte zuerst mit Guard auspacken.

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

Sie werden feststellen, dass unsere Serverkommunikationsfunktion nicht optionale String-Werte als Parameter verwendet, daher wird der Guard zuvor ausgepackt. Das Auspacken ist etwas unintuitiv, da wir es gewohnt sind, mit if let auszupacken, wodurch Werte für die Verwendung innerhalb eines Blocks ausgepackt werden. Hier ist der Guard-Anweisung ein Block zugeordnet, aber es handelt sich tatsächlich um einen else-Block. Wenn Sie also das Entpacken fehlschlagen, werden die Werte direkt in den gleichen Kontext wie die Anweisung selbst entpackt.

// Trennung von Bedenken

Ohne Wache

Ohne Schutz würden wir einen großen Haufen Code erhalten, der a ähnelt Pyramide des Untergangs . Dies lässt sich nicht gut skalieren, um unserem Formular neue Felder hinzuzufügen oder um gut lesbaren Code zu erhalten. Einrückungen können schwierig zu befolgen sein, insbesondere bei so vielen anderen Aussagen an jeder Gabelung.

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

Ja, wir könnten all diese sogar kombinieren, wenn Anweisungen in eine einzige Anweisung durch Kommas getrennt werden, aber wir würden die Fähigkeit verlieren, herauszufinden, welche Anweisung fehlgeschlagen ist, und dem Benutzer eine Nachricht präsentieren.

https://thatthinginswift.com/guard-statement-swift/

Honig
quelle
5

Mit der Verwendung von Guard ist unsere Intensität klar. Wir möchten den Rest des Codes nicht ausführen, wenn diese bestimmte Bedingung nicht erfüllt ist. Auch hier können wir die Kette verlängern. Schauen Sie sich bitte den folgenden Code an:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here
Narendra G.
quelle
5

Wachaussage wird reichen. es ist ein paar verschiedene

1) Es erlaubt mir, verschachtelte if-Anweisungen zu reduzieren.
2) Es erweitert meinen Gültigkeitsbereich, auf den meine Variable zugreifen kann

if-Anweisung

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 
}

Wachaussage

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

}
Nazmul Hasan
quelle
3

Aus der Apple-Dokumentation:

Wacherklärung

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

Synatx:

guard condition else {
    statements
}

Vorteil:

1. Durch die Verwendung von guardAnweisungen können wir tief verschachtelte Bedingungen entfernen, deren einziger Zweck darin besteht, eine Reihe von Anforderungen zu validieren.

2. Es wurde speziell für das frühzeitige Beenden einer Methode oder Funktion entwickelt.

Wenn Sie verwenden, wenn unten angegeben, ist der Code, wie er aussieht.

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

Mit Guard können Sie die Kontrolle aus einem Bereich übertragen, wenn eine oder mehrere Bedingungen nicht erfüllt sind.

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

Referenz:

1. Swift 2: Früh mit Exit beenden 2. Udacity 3. Guard Statement

Ashok R.
quelle
Aber Sie können Letzteres mit if-Anweisungen tun? if condition { return }mürrisch?
Oliver Dixon
2

Wie eine if-Anweisung führt guard Anweisungen basierend auf einem Booleschen Wert eines Ausdrucks aus. Im Gegensatz zu einer if-Anweisung werden Guard-Anweisungen nur ausgeführt, wenn die Bedingungen nicht erfüllt sind. Sie können sich eine Wache eher wie eine Behauptung vorstellen, aber anstatt zu stürzen, können Sie sie elegant beenden.

Siehe: http://ericcerney.com/swift-guard-statement/

Zgpeace
quelle
1

Es macht den Ablauf einer Sequenz mit mehreren Suchvorgängen und Optionen wirklich sehr viel präziser und klarer und reduziert viele Verschachtelungen. Siehe Erica Sadun Beitrag zum Ersetzen von Ifs . .... Könnte mitgerissen werden, ein Beispiel unten:

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

Sehen Sie, ob das klebt.

DavidS
quelle
1

Einfach ausgedrückt bietet es eine Möglichkeit, Felder vor der Ausführung zu validieren. Dies ist ein guter Programmierstil, da er die Lesbarkeit verbessert. In anderen Sprachen könnte es so aussehen:

func doSomething() {
    if something == nil {
        // return, break, throw error, etc.
    }
    ...
}

Da Swift Ihnen jedoch Optionen zur Verfügung stellt, können wir nicht prüfen, ob es Null ist, und seinen Wert einer Variablen zuweisen. Im Gegensatz dazu wird if letüberprüft, ob es nicht Null ist, und eine Variable zugewiesen , die den tatsächlichen Wert enthält. Hier guardkommt das Spiel ins Spiel. Es gibt Ihnen eine präzisere Möglichkeit, frühzeitig mit Optionen zu beenden.

gunby
quelle
1

Quelle: Wache in Swift

Sehen wir uns das Beispiel an, um es klar zu verstehen

Beispiel 1:

func validate() {         
    guard 3>2 else {             
    print ("False")             
    return         
    }         
    print ("True") //True     
} 
validate()

Im obigen Beispiel sehen wir, dass 3 größer als 2 ist und die Anweisung in der Guard-else-Klausel übersprungen und True gedruckt wird.

Beispiel 2:

func validate() {         
    guard 1>2 else {             
    print ("False")            //False 
    return         
    }         
    print ("True")      
} 
validate()

Im obigen Beispiel sehen wir, dass 1 kleiner als 2 ist und die Anweisung in der Guard-else-Klausel ausgeführt wird und False gedruckt wird, gefolgt von 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: "")

Im obigen Beispiel verwenden wir guard let, um die Optionen auszupacken. In der Funktion getName haben wir eine Variable vom Typ string myName definiert, die optional ist. Wir verwenden dann guard let, um zu überprüfen, ob die Variable myName null ist oder nicht. Wenn Sie nicht name zuweisen und erneut prüfen, ist name nicht leer. Wenn beide Bedingungen qualifiziert sind, dh wahr, wird der else-Block übersprungen und "Bedingungen sind mit Namen erfüllt" gedruckt.

Grundsätzlich prüfen wir hier zwei durch Komma getrennte Dinge, zuerst das Auspacken und das optionale, und prüfen, ob dies die Bedingung erfüllt oder nicht.

Hier übergeben wir nichts an die Funktion, dh eine leere Zeichenfolge, und daher ist die Bedingung false der Druck.

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

Hier übergeben wir "Hallo" an die Funktion und Sie können sehen, dass die Ausgabe "Bedingung ist erfüllt Hallo" gedruckt wird.

Aditya
quelle