Słowo kluczowe strażnika Swift

197

Swift 2 wprowadził guardsłowo kluczowe, którego można użyć, aby upewnić się, że różne dane są skonfigurowane gotowe do pracy. Przykład, który widziałem na tej stronie demonstruje funkcję submTapped:

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

    print("All good")
}

Zastanawiam się, czy używanie guardjest czymś innym niż robienie tego w staromodny sposób, przy użyciu ifwarunku. Czy daje to korzyści, których nie można uzyskać za pomocą prostego czeku?

David Snabel
źródło
Zobacz także pytanie
Honey
Proszę odnieść się do następującego linku medium.com/@pvpriya7/swift-guard-18e59c50c624
Priyanka V

Odpowiedzi:

368

Czytając ten artykuł , zauważyłem wspaniałe korzyści ze stosowania Guard

Tutaj możesz porównać użycie warty z przykładem:

Oto część bez osłony:

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

    // Value requirements not met, do something
}
  1. Tutaj umieszczasz żądany kod we wszystkich warunkach

    Być może nie zauważysz od razu problemu z tym, ale możesz sobie wyobrazić, jak może być mylące, jeśli zostanie zagnieżdżone w wielu warunkach, które muszą zostać spełnione przed uruchomieniem instrukcji

Sposobem na oczyszczenie tego jest wykonanie każdego z czeków w pierwszej kolejności i wyjście, jeśli nie zostaną spełnione. Pozwala to na łatwe zrozumienie, jakie warunki spowodują wyjście z tej funkcji.

Ale teraz możemy użyć straży i widzimy, że można rozwiązać niektóre problemy:

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. Sprawdzanie stanu, który chcesz, a nie tego, którego nie chcesz. To znowu jest podobne do twierdzenia. Jeśli warunek nie jest spełniony, uruchamiana jest instrukcja else wartownika, która przerywa działanie funkcji.
  2. Jeśli warunek się spełni, opcjonalna zmienna tutaj jest automatycznie rozpakowywana w zakresie, w którym wywołano instrukcję guard - w tym przypadku funkcja fooGuard (_ :).
  3. Wczesne sprawdzanie złych przypadków, dzięki czemu twoja funkcja jest bardziej czytelna i łatwiejsza w utrzymaniu

Ten sam wzorzec obowiązuje również w przypadku wartości nieobowiązkowych:

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
}

Jeśli nadal masz pytania, możesz przeczytać cały artykuł: Szybka wypowiedź wartownika.

Podsumowanie

I na koniec, czytając i testując, odkryłem, że jeśli używasz wartownika, aby rozpakować dowolne opcje,

te nieopakowane wartości pozostają w pobliżu, abyś mógł ich użyć w pozostałej części kodu

.

guard let unwrappedName = userName else {
    return
}

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

Tutaj rozpakowana wartość byłaby dostępna tylko w bloku 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)")
Jorge Casariego
źródło
3
Hej @ Eric, zrobiłeś świetny post! Dziękujemy za uczynienie tego tak łatwym do zrozumienia!
Jorge Casariego
1
Używam strażnika do rozpakowywania NSError. Ale kiedy próbuję użyć go w zakresie ochronnym (na przykład w celu przekazania błędu do jakiegoś wywołania zwrotnego), mówi „Zmienna zadeklarowana w warunku ochronnym nie jest użyteczna w jego ciele”. Czy to ma jakiś sens? Dzięki
GeRyCh
6
Rozpakowanie @GeRyCh w instrukcji guard udostępnia tę zmienną dla instrukcji po instrukcji guard, a nie w jej obrębie. Zajęło mi trochę czasu, aby się do tego przyzwyczaić.
DonnaLea,
2
Oto kolejny doskonały artykuł na temat używania osłony do czystego rozpakowywania opcji. Ładnie podsumowuje.
Doches
let x = x where x > 0czy oznacza to, że połączyłeś inny warunek z opcjonalnym wiązaniem? To znaczy, że jest trochę inaczej niżif let constantName = someOptional { statements }
Honey
36

W przeciwieństwie if, guardtworzy zmienną, które mogą być dostępne z zewnątrz jej bloku. Przydatne jest rozpakowanie wielu Optionals.

takebayashi
źródło
24

Są naprawdę dwie duże korzyści guard. Jednym z nich jest unikanie piramidy zagłady, jak wspomnieli inni - wiele irytujących if letstwierdzeń zagnieżdżonych w sobie, poruszających się coraz bardziej w prawo.

Inną korzyścią jest często logika, którą chcesz wdrożyć: „if not let ” niż „ if let { } else”.

Oto przykład: załóżmy, że chcesz zaimplementować accumulate- krzyżowanie mapi miejsce, w reducektórym zwraca tablicę uruchomień, zmniejsza. Oto on z 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(+)  // []

Jak napisałbyś to bez ochrony, ale nadal używanie firsttego zwraca opcjonalne? Coś takiego:

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

}

Dodatkowe zagnieżdżanie jest denerwujące, ale też nie jest tak logiczne, aby rozdzielić ifi elsedotychczas. O wiele bardziej czytelne jest wczesne wyjście z pustej skrzynki, a następnie kontynuacja reszty funkcji, jakby to nie była możliwość.

Prędkość lotu
źródło
19

Gdy warunek jest spełniony przy użyciu guard, ujawnia zmienne zadeklarowane w guardbloku reszcie bloku kodu, wprowadzając je do swojego zakresu. Co, jak wcześniej wspomniano, z pewnością przyda się w przypadku zagnieżdżonych if letinstrukcji.

Zauważ, że wartownik wymaga zwrotu lub rzutu w swojej instrukcji else.

Przetwarzanie JSON ze Strażnikiem

Poniżej znajduje się przykład tego, w jaki sposób można parsować obiekt JSON za pomocą wartownika zamiast if-let. To jest fragment wpisu na blogu, który zawiera plik placu zabaw, który można znaleźć tutaj:

Jak korzystać ze Strażnika w Swift 2, aby analizować 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)

}

pobierz plac zabaw: straży

Więcej informacji:

Oto fragment Przewodnika języka programowania Swift:

Jeśli warunek instrukcji guard jest spełniony, wykonywanie kodu jest kontynuowane po nawiasie zamykającym instrukcji guard. Wszelkie zmienne lub stałe, którym przypisano wartości za pomocą opcjonalnego powiązania w ramach warunku, są dostępne dla pozostałej części bloku kodu, w którym pojawia się instrukcja guard.

Jeśli ten warunek nie jest spełniony, wykonywany jest kod wewnątrz gałęzi else. Ta gałąź musi przekazać kontrolę, aby wyjść z bloku kodu, w którym pojawia się ta instrukcja wart. Może to zrobić za pomocą instrukcji przekazania kontroli, takiej jak return, break, lub kontynuować, lub może wywołać funkcję lub metodę, która nie zwraca, as fatalError ().

Dan Beaulieu
źródło
7

Jedną z korzyści jest wyeliminowanie wielu zagnieżdżonych if letinstrukcji. Obejrzyj film WWDC „What's New in Swift” około 15:30, sekcja zatytułowana „Pyramid of Doom”.

zaph
źródło
6

Kiedy używać strażników

Jeśli masz kontroler widoku z kilkoma elementami UITextField lub innym rodzajem danych wprowadzanych przez użytkownika, natychmiast zauważysz, że musisz rozpakować opcjonalny textField.text, aby dostać się do tekstu w środku (jeśli istnieje!). isEmpty nie zrobi tu nic dobrego, bez żadnego wpisu pole tekstowe po prostu zwróci zero.

Masz więc kilka z nich, które rozpakowujesz i ostatecznie przekazujesz do funkcji, która wysyła je do punktu końcowego serwera. Nie chcemy, aby kod serwera miał do czynienia z zerowymi wartościami lub przez pomyłkę wysyłał nieprawidłowe wartości do serwera, więc najpierw rozpakujemy te wartości wejściowe za pomocą wartownika.

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

Zauważysz, że nasza funkcja komunikacji z serwerem przyjmuje jako parametry nie opcjonalne wartości ciągu, dlatego przedtem rozpakuj straż. Rozpakowywanie jest trochę nieintuicyjne, ponieważ jesteśmy przyzwyczajeni do rozpakowywania, jeśli pozwala, które rozpakowuje wartości do użycia w bloku. W tym przypadku instrukcja guard ma powiązany blok, ale w rzeczywistości jest to blok inny - tzn. Czynność wykonywana w przypadku niepowodzenia rozpakowywania - wartości są rozpakowywane bezpośrednio w tym samym kontekście, co sama instrukcja.

// rozdzielenie obaw

Bez ochrony

Bez użycia straży otrzymalibyśmy wielki stos kodu, który przypomina piramidę zagłady . Nie skaluje się to dobrze do dodawania nowych pól do naszego formularza ani do tworzenia bardzo czytelnego kodu. Wcięcie może być trudne do naśladowania, szczególnie przy wielu innych stwierdzeniach przy każdym rozwidleniu.

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

Tak, moglibyśmy nawet połączyć je wszystkie, jeśli pozwolimy na instrukcje w jedną instrukcję oddzieloną przecinkami, ale stracilibyśmy możliwość stwierdzenia, która instrukcja się nie powiodła i przedstawienia użytkownikowi komunikatu.

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

kochanie
źródło
5

Dzięki użyciu osłony nasze intencje są jasne. nie chcemy wykonywać reszty kodu, jeśli ten szczególny warunek nie jest spełniony. tutaj możemy również rozszerzyć łańcuch, spójrz na poniższy kod:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here
Narendra G.
źródło
5

Strażnik zamierza zrobić. jest kilka różnych

1) pozwala mi zmniejszyć zagnieżdżone, jeśli instrukcja
2) zwiększyć zakres, do którego moja zmienna jest dostępna

Instrukcja if

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 
}

Strażnik

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
źródło
3

Z dokumentacji Apple:

Strażnik

Instrukcja straży służy do przeniesienia kontroli programu poza zakres, jeśli nie jest spełniony co najmniej jeden warunek.

Synatx:

guard condition else {
    statements
}

Korzyść:

1. Za pomocą guardinstrukcji możemy pozbyć się głęboko zagnieżdżonych instrukcji warunkowych, których jedynym celem jest sprawdzanie poprawności zestawu wymagań.

2. Został zaprojektowany specjalnie do wcześniejszego opuszczenia metody lub funkcji.

jeśli użyjesz, jeśli poniżej znajduje się kod, jak to wygląda.

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

Za pomocą wartownika możesz przenieść kontrolę poza zakres, jeśli nie jest spełniony jeden lub więcej warunków.

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

Odniesienie:

1. Swift 2: Wyjdź wcześnie ze strażnikiem 2. Udacity 3. Strażnik

Ashok R.
źródło
Ale możesz to zrobić za pomocą instrukcji if? if condition { return }gburowaty?
Oliver Dixon
2

Podobnie jak instrukcja if, guard wykonuje instrukcje na podstawie logicznej wartości wyrażenia. W przeciwieństwie do instrukcji if, instrukcje guard działają tylko wtedy, gdy warunki nie są spełnione. Możesz myśleć o straży bardziej jak Assert, ale zamiast upaść, możesz z wdziękiem wyjść.

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

Zgpeace
źródło
1

Naprawdę naprawdę sprawia, że ​​przepływ sekwencji z kilkoma przeglądami i opcjami jest o wiele bardziej zwięzły i przejrzysty i zmniejsza wiele zagnieżdżania. Zobacz post Erica Sadun na temat zastępowania Ifs . .... Może dać się ponieść, przykład poniżej:

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

Sprawdź, czy to się trzyma.

DavidS
źródło
1

Mówiąc najprościej, zapewnia sposób sprawdzania poprawności pól przed wykonaniem. To dobry styl programowania, ponieważ zwiększa czytelność. W innych językach może to wyglądać tak:

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

Ponieważ jednak Swift udostępnia opcje, nie możemy sprawdzić, czy jest zero, i przypisać jego wartość do zmiennej. W przeciwieństwie do if lettego sprawdza, czy nie jest zero, i przypisuje zmienną do przechowywania wartości rzeczywistej. Tu właśnie guardwchodzi w grę. Daje ci bardziej zwięzły sposób wychodzenia z wcześniejszych opcji.

Gunby
źródło
1

Źródło: Guard in Swift

Zobaczmy przykład, aby jasno to zrozumieć

Przykład 1:

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

W powyższym przykładzie widzimy, że 3 jest większe niż 2 i instrukcja wewnątrz klauzuli guard else jest pomijana i wypisywana jest prawda.

Przykład 2:

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

W powyższym przykładzie widzimy, że 1 jest mniejsze niż 2 i instrukcja wewnątrz klauzuli guard else jest wykonywana, a fałsz jest drukowany, a następnie zwracany.

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

W powyższym przykładzie używamy wartownika let, aby rozpakować opcje. W funkcji getName zdefiniowaliśmy zmienną typu string myName, która jest opcjonalna. Następnie używamy wartownika let, aby sprawdzić, czy zmienna myName ma wartość zero, czy nie, jeśli nie zostanie przypisana do nazwy i sprawdzi ponownie, nazwa nie jest pusta. Jeśli oba warunki zostaną spełnione, tzn. Prawda, blok else zostanie pominięty i zostanie wyświetlony komunikat „Warunki są spełnione z nazwą”.

Zasadniczo sprawdzamy tutaj dwie rzeczy oddzielone przecinkiem, najpierw rozpakowujemy i opcjonalnie i sprawdzamy, czy spełnia to warunek, czy nie.

Tutaj nie przekazujemy nic do funkcji, tj. Pusty ciąg, a zatem Warunek jest fałszywy, to druk.

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

Tutaj przekazujemy funkcję „Hello” do funkcji i widać, że wydrukowany jest komunikat „Warunek jest spełniony Witaj”.

Aditya
źródło