Swift: guard let vs if let


132

Ho letto degli Optionals in Swift e ho visto esempi in cui if letviene utilizzato per verificare se un Optional contiene un valore e, nel caso lo sia, fare qualcosa con il valore non imballato.

Tuttavia, ho visto che in Swift 2.0 la parola chiave guard letviene utilizzata principalmente. Mi chiedo se if letsia stato rimosso da Swift 2.0 o se sia ancora possibile utilizzarlo.

Devo cambiare i miei programmi che contengono if letin guard let?

Risposte:


165

if lete guard letservono a scopi simili, ma distinti.

Il caso "else" di guarddeve uscire dall'ambito corrente. Generalmente questo significa che deve chiamare returno interrompere il programma. guardviene utilizzato per fornire un ritorno anticipato senza richiedere l'annidamento del resto della funzione.

if letannida il suo ambito e non richiede nulla di speciale da esso. Può returno no.

In generale, se il if-letblocco fosse il resto della funzione, o la sua elseclausola avesse un returno interrompesse, allora dovresti usare guardinvece. Questo spesso significa (almeno nella mia esperienza), in caso di dubbio, di guardsolito è la risposta migliore. Ma ci sono molte situazioni in cui if letè ancora appropriato.


39
Utilizzare if letquando il non-nilcaso è valido. Da utilizzare guardquando il nilcaso rappresenta una sorta di errore.
Ballpoint

4
@ BallpointBen Non sono d'accordo con questo. Ci sono molti casi in cui guardè appropriato anche se non ci sono errori. A volte significa semplicemente che non c'è niente da fare. Ad esempio, un positionTitlemetodo potrebbe guard if let title = title else {return}. Il titolo può essere facoltativo, nel qual caso non si tratta di un errore. Ma guard letè ancora appropriato.
Rob Napier

1
Si; Volevo dire che la guardia ha lasciato entrare il commento.
Rob Napier

1
in altre parole, "guard let" viene utilizzato quando il codice è sicuro al 99% di non utilizzare il condizionale else; d'altra parte, "if let" quando il codice è 50 - 50 (esempio) per usare la condizione else.
Chino Pan

1
La variabile vincolata da if letè visibile solo all'interno if let dell'ambito. La variabile vincolata da guard letè visibile successivamente. Quindi ha senso usare guard per associare anche valori opzionali.
boweidmann

106

La guardia può migliorare la chiarezza

Quando usi la guardia hai un'aspettativa molto più alta che la guardia abbia successo ed è in qualche modo importante che se non riesce, allora vuoi uscire dall'ambito in anticipo . Come se tu guardassi per vedere se esiste un file / immagine, se un array è vuoto o meno.

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
}

Se scrivi il codice sopra con if-let, comunica allo sviluppatore di lettura che è più di un 50-50. Ma se usi guard aggiungi chiarezza al tuo codice e ciò implica che mi aspetto che funzioni il 95% delle volte ... se mai fallisse, non so perché dovrebbe; è molto improbabile ... ma usa semplicemente questa immagine predefinita o forse semplicemente asserisci con un messaggio significativo che descrive cosa è andato storto!

  • Evitare guardquando creano effetti collaterali, le protezioni devono essere utilizzate come un flusso naturale . Evita le guardie quando le elseclausole introducono effetti collaterali. Le protezioni stabiliscono le condizioni richieste affinché il codice venga eseguito correttamente, offrendo un'uscita anticipata

  • Quando si esegue un calcolo significativo nel ramo positivo, refactoring da ifa guardun'istruzione e restituisce il valore di fallback nella elseclausola

Da: il libro Swift Style di Erica Sadun

Inoltre, come risultato dei suggerimenti di cui sopra e del codice pulito, è più probabile che tu voglia / avrai bisogno di aggiungere asserzioni nelle dichiarazioni di guardia non riuscite , migliora solo la leggibilità e rende chiaro agli altri sviluppatori cosa ti aspettavi.

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

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

Da: il libro Swift Style di Erica Sadun + alcune modifiche

(non utilizzerai asserzioni / precondizioni per if-lets. Semplicemente non sembra giusto)

L'uso delle guardie ti aiuta anche a migliorare la chiarezza evitando la piramide del destino. Vedi la risposta di Nitin .


Guard crea una nuova variabile

C'è una differenza importante che credo nessuno abbia spiegato bene.

Entrambi guard lete if let scartare comunque la variabile

Con guard letstai creando una nuova variabile che esisterà al di fuori elsedell'istruzione.

Con if letnon stai creando alcuna nuova variabile, dopo l'istruzione else, inserisci il blocco di codice solo se l'opzionale è diverso da zero. La variabile appena creata esiste solo all'interno del blocco di codice non dopo!

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

Per ulteriori informazioni if let, vedere: Perché la ridichiarazione dell'associazione facoltativa non crea un errore


La protezione richiede l' uscita dall'ambito

(Menzionato anche nella risposta di Rob Napier):

È NECESSARIO aver guarddefinito all'interno di una funzione. Lo scopo principale è interrompere / restituire / uscire dall'ambito, se una condizione non è soddisfatta:

var str : String?

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

Perché if letnon è necessario averlo all'interno di nessuna funzione:

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

guard vs if

Vale la pena di notare che è più appropriato per vedere questa domanda in quanto guard letvs if lete guardcontro if.

Uno standalone ifnon esegue alcuno scartamento, né lo fa uno standalone guard. Vedi esempio sotto. Non esce presto se un valore è nil. Non ci sono valori opzionali. Esce solo in anticipo se una condizione non viene soddisfatta.

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

46

Quando usare if-lete quando usare guardè spesso una questione di stile.

Supponiamo di avere func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intun array opzionale di elementi ( var optionalArray: [SomeType]?) e di dover restituire 0se l'array è nil(non impostato) o countse l'array ha un valore (è impostato).

Potresti implementarlo in questo modo usando if-let:

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

o in questo modo usando guard:

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

Gli esempi sono funzionalmente identici.

Quello guardche brilla davvero è quando hai un'attività come la convalida dei dati e vuoi che la funzione fallisca presto se qualcosa non va.

Invece di annidare un mucchio di messaggi di posta if-letelettronica man mano che ci si avvicina alla fine della convalida, il "percorso di successo" e gli optionals ora associati con successo sono tutti nell'ambito principale del metodo, perché i percorsi di errore sono già tutti restituiti.


30

Cercherò di spiegare l'utilità delle istruzioni guard con del codice (non ottimizzato).

Hai un'interfaccia utente in cui convalidi i campi di testo per la registrazione dell'utente con nome, cognome, e-mail, telefono e password.

Se qualsiasi textField non contiene testo valido, dovrebbe rendere quel campo firstResponder.

ecco il codice non ottimizzato:

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

Puoi vedere sopra, che tutte le stringhe (firstNameString, lastNameString ecc.) Sono accessibili solo nell'ambito dell'istruzione if. quindi crea questa "piramide del destino" e ha molti problemi con essa, inclusa la leggibilità e la facilità di spostare le cose (se l'ordine dei campi è alterato, devi riscrivere la maggior parte di questo codice)

Con l'istruzione guard (nel codice sottostante), puoi vedere che queste stringhe sono disponibili al di fuori di {}e vengono utilizzate, se tutti i campi sono validi.

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

Se l'ordine dei campi cambia, sposta semplicemente le rispettive righe di codice in alto o in basso e sei a posto.

Questa è una spiegazione molto semplice e un caso d'uso. Spero che questo ti aiuti!


14

Differenza di base

Guardia

  1. I primi processi esistono dall'ambito
  2. Richiedi punteggio esistente come ritorno, lancio ecc.
  3. Crea una nuova variabile a cui puoi accedere dall'ambito.

se lasciate

  1. Impossibile accedere al campo di applicazione.
  2. non è necessario restituire la dichiarazione. Ma possiamo scrivere

NOTA: entrambi vengono utilizzati per scartare la variabile facoltativa.


2

La spiegazione più chiara che ho visto era nella Github Swift Style Guide :

if aggiunge un livello di profondità:

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

guard non lo fa:

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

2

guardia

  • Una guarddichiarazione viene utilizzato per il controllo del programma di trasferimento di un campo di applicazione, se una o più condizioni non sono soddisfatte.

  • Il valore di qualsiasi condizione in guardun'istruzione deve essere di tipo Bool o un tipo a cui è collegato Bool. La condizione può anche essere una dichiarazione di associazione facoltativa.

Una dichiarazione di guardia ha la seguente forma:

guard condition else {
    //Generally return
}

se lasciate

if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}

1

L'ho imparato da Swift con Bob ..

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

Problemi con Else-If

  1. Parentesi annidate
  2. Devo leggere ogni riga per individuare il messaggio di errore

Istruzione Guard Un blocco guard viene eseguito solo se la condizione è falsa e uscirà dalla funzione tramite return. Se la condizione è vera, Swift ignora il blocco di guardia. Fornisce un'uscita anticipata e meno parentesi. +

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

Unwrap Optionals con Else-If

Un'istruzione guard non è solo utile per sostituire un tipico blocco condizionale con un'istruzione else-if, ma è anche ottima per scartare gli optionals riducendo al minimo il numero di parentesi. Per confrontare, iniziamo prima come scartare più optionals con else-if. Per prima cosa, creiamo tre optionals che verranno scartati.

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

Il peggior incubo

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

Il codice di cui sopra funziona certamente ma viola il principio DRY. È atroce. Analizziamolo. +

Leggermente migliore Il codice seguente è più leggibile di quello precedente. +

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

Unwrap with Guard Le istruzioni else-if possono essere sostituite con 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)
         }

Scartare più opzioni con Else-If Finora, hai scartato gli optional uno per uno. Swift ci consente di scartare più optional contemporaneamente. Se uno di loro contiene zero, eseguirà il blocco 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")
  }
}

Tieni presente che quando scarti più optionals contemporaneamente, non puoi identificare quale contiene nil

Scartare più opzioni con Guard Ovviamente, dovremmo usare guard over else-if. +

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
}

torna indietro e correggi la formattazione / rientro del codice!
pkamb
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.