Parola chiave di guardia di Swift


197

Swift 2 ha introdotto la guardparola chiave, che potrebbe essere utilizzata per garantire che vari dati siano configurati pronti all'uso. Un esempio che ho visto su questo sito web dimostra una funzione submitTapped:

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

    print("All good")
}

Mi chiedo se usare guardsia diverso dal farlo alla vecchia maniera, usando una ifcondizione. Offre vantaggi che non è possibile ottenere utilizzando un semplice controllo?


Vedi anche la domanda guard vs if-let
Honey

Si prega di fare riferimento al seguente link medium.com/@pvpriya7/swift-guard-18e59c50c624
Priyanka V

Risposte:


368

Leggendo questo articolo ho notato grandi benefici usando Guard

Qui puoi confrontare l'uso di guard con un esempio:

Questa è la parte senza guardia:

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

    // Value requirements not met, do something
}
  1. Qui stai inserendo il codice desiderato in tutte le condizioni

    Potresti non vedere immediatamente un problema con questo, ma potresti immaginare quanto possa diventare confuso se fosse nidificato con numerose condizioni che tutti dovevano essere soddisfatte prima di eseguire le tue dichiarazioni

Il modo per ripulire è quello di fare prima ciascuno dei tuoi controlli e uscire se non sono stati soddisfatti. Ciò consente una facile comprensione di quali condizioni faranno uscire questa funzione.

Ma ora possiamo usare la guardia e possiamo vedere che è possibile risolvere alcuni problemi:

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. Verifica la condizione che desideri, non quella che non desideri. Anche questo è simile a un'affermazione. Se la condizione non viene soddisfatta, viene eseguita l'istruzione else di guard, che esce dalla funzione.
  2. Se la condizione passa, la variabile opzionale qui viene automaticamente scartata per te nell'ambito che è stata chiamata l'istruzione guard - in questo caso, la funzione fooGuard (_ :).
  3. Stai verificando in anticipo casi negativi, rendendo la tua funzione più leggibile e più facile da mantenere

Lo stesso modello vale anche per valori non opzionali:

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
}

Se hai ancora domande, puoi leggere l'intero articolo: Swift guard statement.

Avvolgendo

E infine, leggendo e testando, ho scoperto che se usi guard per scartare qualsiasi opzione,

quei valori da scartare restano in giro per te da usare nel resto del tuo blocco di codice

.

guard let unwrappedName = userName else {
    return
}

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

Qui il valore da scartare sarebbe disponibile solo all'interno del blocco 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)")

3
Ehi @Eric hai fatto un post eccellente! Grazie a te per averlo reso così facile da capire!
Jorge Casariego,

1
Sto usando la protezione per scartare NSError. Ma quando provo ad usarlo nell'ambito di guardia (per esempio per passare un errore in un callback), dice "La variabile dichiarata in condizione di guardia non è utilizzabile nel suo corpo". Ha senso? Grazie
GeRyCh il

6
@GeRyCh scartandolo in un'istruzione guard rende disponibile quella variabile per dopo l'istruzione guard, non al suo interno. Mi ci è voluto un po 'per abituarmi a questo.
DonnaLea,

2
Ecco un altro eccellente articolo sull'uso della protezione per scartare gli optional in modo pulito. Riassume bene.
Doches

let x = x where x > 0significa che hai accoppiato un'altra condizione nella tua rilegatura opzionale? Voglio dire, è un po 'diverso daif let constantName = someOptional { statements }
Honey,

36

Diversamente if, guardcrea la variabile a cui è possibile accedere dall'esterno del suo blocco. È utile scartare molte Optionals.


24

Ci sono davvero due grandi vantaggi guard. Uno sta evitando la piramide del destino, come altri hanno già detto: molte if letaffermazioni fastidiose annidate l'una nell'altra si spostano sempre più a destra.

L'altro vantaggio è spesso che la logica che si desidera implementare è più " if not let" che " if let { } else".

Ecco un esempio: supponiamo che tu voglia implementare accumulate- un incrocio tra mape reducedove ti restituisce un array di corsa riduce. Eccolo con 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(+)  // []

Come lo scriveresti senza protezione, ma continuando a usarlo firstrestituisce un facoltativo? Qualcosa come questo:

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'annidamento extra è fastidioso, ma anche, non è logico avere l'uno ife l'altro elsecosì distanti. È molto più leggibile avere l'uscita anticipata per il caso vuoto, quindi continuare con il resto della funzione come se non fosse possibile.


19

Quando viene soddisfatta una condizione che utilizza guardespone le variabili dichiarate all'interno del guardblocco al resto del blocco di codice, portandole nel suo ambito. Che, come precedentemente affermato, tornerà sicuramente utile con le if letdichiarazioni nidificate .

Nota che la guardia richiede un ritorno o un tiro nella sua dichiarazione else.

Analisi di JSON con Guard

Di seguito è riportato un esempio di come si potrebbe analizzare un oggetto JSON usando guard piuttosto che if-let. Questo è un estratto da un post di blog che include un file di parco giochi che puoi trovare qui:

Come usare Guard in Swift 2 per analizzare 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)

}

scarica parco giochi: parco giochi di guardia

Ulteriori informazioni:

Ecco un estratto dalla Guida al linguaggio di programmazione Swift:

Se le condizioni dell'istruzione guard sono soddisfatte, l'esecuzione del codice continua dopo la parentesi graffa di chiusura dell'istruzione guard. Qualsiasi variabile o costante a cui sono stati assegnati valori usando un'associazione opzionale come parte della condizione sono disponibili per il resto del blocco di codice in cui appare l'istruzione guard.

Se tale condizione non viene soddisfatta, viene eseguito il codice all'interno del ramo else. Quel ramo deve trasferire il controllo per uscire dal blocco di codice in cui appare l'istruzione guard. Può farlo con un'istruzione di trasferimento del controllo come return, break o continue oppure può chiamare una funzione o un metodo che non restituisce, come come fatalError ().


7

Un vantaggio è l'eliminazione di molte if letaffermazioni nidificate . Guarda il video "Novità di Swift" del WWDC intorno alle 15:30, la sezione intitolata "Pyramid of Doom".


6

Quando usare le protezioni

Se hai un controller di visualizzazione con alcuni elementi UITextField o qualche altro tipo di input dell'utente, noterai immediatamente che devi scartare il textField.text opzionale per accedere al testo (se presente!). isEmpty non ti farà nulla di buono qui, senza alcun input il campo di testo restituirà semplicemente zero.

Quindi ne hai alcuni che scarterai e alla fine passerai a una funzione che li registra su un endpoint del server. Non vogliamo che il codice del server abbia a che fare con valori nulli o invii erroneamente valori non validi al server, quindi scarteremo prima quei valori di input con 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) {
  ...
}

Noterai che la nostra funzione di comunicazione del server accetta valori String non opzionali come parametri, quindi la protezione non viene scartata in anticipo. Lo scartare è un po 'poco intuitivo perché siamo abituati a scartare con if let che scartano i valori da usare all'interno di un blocco. Qui l'istruzione guard ha un blocco associato ma in realtà è un altro blocco - cioè la cosa che fai se lo scartamento fallisce - i valori vengono scartati direttamente nello stesso contesto dell'istruzione stessa.

// separazione degli interessi

Senza guardia

Senza usare la guardia, finiremmo con una grande pila di codice che ricorda una piramide di sventura . Questo non si adatta bene per l'aggiunta di nuovi campi al nostro modulo o rende il codice molto leggibile. Il rientro può essere difficile da seguire, in particolare con tante altre dichiarazioni a ogni fork.

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

Sì, potremmo anche combinare tutte queste affermazioni se lasciamo dichiarazioni in una singola istruzione separata da virgole, ma perderemmo la capacità di capire quale affermazione non è riuscita e presentare un messaggio all'utente.

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


5

Con l'utilizzo della guardia la nostra intensione è chiara. non vogliamo eseguire il resto del codice se quella particolare condizione non è soddisfatta. qui siamo in grado di estendere anche la catena, per favore dai un'occhiata al codice qui sotto:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here

5

Dichiarazione della guardia sta per fare. è un paio di diversi

1) mi permette di ridurre l'istruzione nidificata se
2) è aumentare il mio ambito a cui la mia variabile accessibile

se Dichiarazione

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 
}

Dichiarazione della guardia

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

}

3

Dalla documentazione Apple:

Dichiarazione di guardia

Un'istruzione guard viene utilizzata per trasferire il controllo del programma da un ambito se una o più condizioni non sono soddisfatte.

Synatx:

guard condition else {
    statements
}

Vantaggio:

1. Usando la guarddichiarazione possiamo sbarazzarci di condizionali profondamente annidati il ​​cui unico scopo è la validazione di una serie di requisiti.

2. È stato progettato specificamente per uscire anticipatamente da un metodo o una funzione.

se si utilizza se let sotto è il codice come appare.

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

Usando guard puoi trasferire il controllo da un ambito se una o più condizioni non sono soddisfatte.

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

Riferimento:

1. Swift 2: Esci presto con la guardia 2. Udacity 3. Dichiarazione di guardia


Ma puoi fare quest'ultimo con le dichiarazioni if? if condition { return }burbero?
Oliver Dixon,

2

Come un'istruzione if, guard esegue istruzioni basate su un valore booleano di un'espressione. A differenza di un'istruzione if, le istruzioni guard vengono eseguite solo se le condizioni non sono soddisfatte. Puoi pensare alla guardia più come un Assert, ma invece di schiantarti, puoi uscire con grazia.

fare riferimento a: http://ericcerney.com/swift-guard-statement/


1

Rende davvero il flusso di una sequenza con diverse ricerche e opzioni molto più conciso e chiaro e riduce molto se la nidificazione. Vedi il post di Erica Sadun sulla sostituzione di Ifs . .... Potrebbe essere portato via, un esempio qui sotto:

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

Vedi se si attacca.


1

In poche parole, fornisce un modo per convalidare i campi prima dell'esecuzione. Questo è un buon stile di programmazione in quanto migliora la leggibilità. In altre lingue, potrebbe apparire così:

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

Ma poiché Swift ti offre degli optional, non possiamo verificare se è zero e assegnare il suo valore a una variabile. Al contrario, if letverifica che non sia nulla e assegna una variabile per contenere il valore effettivo. È qui che guardentra in gioco. Ti dà un modo più conciso di uscire presto usando gli opzionali.


1

Fonte: Guard in Swift

Vediamo l'esempio per capirlo chiaramente

Esempio 1:

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

Nell'esempio sopra vediamo che 3 è maggiore di 2 e l'istruzione all'interno della clausola guard else viene saltata e True viene stampato.

Esempio 2:

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

Nell'esempio sopra vediamo che 1 è più piccolo di 2 e l'istruzione all'interno della clausola guard else viene eseguita e False viene stampato seguito da 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: "")

Nell'esempio sopra stiamo usando guard let per scartare gli optionals. Nella funzione getName abbiamo definito una variabile di tipo stringa myName che è facoltativa. Usiamo quindi guard let per verificare se la variabile myName è zero o no, se non assegnare a name e ricontrollare, name non è vuoto. Se entrambe le condizioni sono qualificate, vale a dire vero, il blocco else verrà saltato e verrà stampato "Condizioni soddisfatte con nome".

Fondamentalmente stiamo controllando due cose qui separate da virgola, prima da scartare e facoltative e verificare se questo soddisfa le condizioni o meno.

Qui non stiamo passando nulla alla funzione ovvero stringa vuota e quindi Condizione è falsa è la stampa.

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

Qui stiamo passando "Hello" alla funzione e puoi vedere che l'output è stampato "La condizione è soddisfatta Hello".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.