Swift: guardia let vs if let

130

He estado leyendo acerca de los opcionales en Swift, y he visto ejemplos en los que if letse usa para verificar si un Opcional tiene un valor y, en caso de que lo haga, haga algo con el valor sin envolver.

Sin embargo, he visto que en Swift 2.0 la palabra clave guard letse usa principalmente. Me pregunto si if letse ha eliminado de Swift 2.0 o si todavía se puede usar.

¿Debo cambiar mis programas que contienen if leta guard let?

lmiguelvargasf
fuente

Respuestas:

164

if lety guard letsirven para propósitos similares pero distintos.

El caso "else" de guarddebe salir del ámbito actual. En general, eso significa que debe llamar returno cancelar el programa. guardse usa para proporcionar un retorno temprano sin requerir el anidamiento del resto de la función.

if letanida su alcance y no requiere nada especial de él. Puede returno no.

En general, si el if-letbloque iba a ser el resto de la función, o su elsecláusula tendría un returno abortar, entonces debería usarlo guard. Esto a menudo significa (al menos en mi experiencia), en caso de duda, guardsuele ser la mejor respuesta. Pero hay muchas situaciones en las que if letaún es apropiado.

Rob Napier
fuente
37
Úselo if letcuando el non-nilcaso sea válido. Úselo guardcuando el nilcaso representa algún tipo de error.
BallpointBen
44
@BallpointBen No estoy de acuerdo con eso. Hay muchos casos donde guardes apropiado incluso si no hay error. A veces solo significa que no hay nada que hacer. Por ejemplo, un positionTitlemétodo podría guard if let title = title else {return}. El título puede ser opcional, en cuyo caso esto no es un error. Pero guard letsigue siendo apropiado.
Rob Napier
1
Si; Quise decir guardia dejó entrar el comentario.
Rob Napier el
1
en otras palabras, "guard let" se usa cuando el código está 99% seguro de no usar el else condicional; por otro lado, "if let" cuando el código es 50 - 50 (ejemplo) para usar la condición else.
Chino Pan
1
La variable enlazada if letsolo es visible dentro del if let alcance. La variable enlazada por guard letes visible después. Por lo tanto, tiene sentido usar guard para enlazar valores opcionales también.
Boweidmann
105

Guardia puede mejorar la claridad

Cuando usa guardia, tiene una expectativa mucho mayor de que el guardia tenga éxito y es algo importante que si no tiene éxito, entonces solo desea salir del alcance antes de tiempo . Al igual que guarda para ver si existe un archivo / imagen, si una matriz está vacía o no.

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
}

Si escribe el código anterior con if-let, transmite al desarrollador de lectura que es más de un 50-50. Pero si usa guardia, agrega claridad a su código e implica que espero que funcione el 95% del tiempo ... si alguna vez falla, no sé por qué lo haría; es muy poco probable ... ¡pero simplemente use esta imagen predeterminada en su lugar o tal vez simplemente afirme con un mensaje significativo que describa lo que salió mal!

  • Evite guardcuando crean efectos secundarios, los guardias deben usarse como un flujo natural . Evite los guardias cuando las elsecláusulas introducen efectos secundarios. Los guardias establecen las condiciones requeridas para que el código se ejecute correctamente, ofreciendo una salida anticipada

  • Cuando realiza un cálculo significativo en la rama positiva, refactorice ifa una guardinstrucción y devuelva el valor de reserva en la elsecláusula

Del libro de estilo rápido de Erica Sadun

Además, como resultado de las sugerencias anteriores y el código limpio, es más probable que desee / necesite agregar aserciones en las declaraciones de protección fallidas , solo mejora la legibilidad y deja en claro a otros desarrolladores lo que esperaba.

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

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

De: Libro Swift Style de Erica Sadun + algunas modificaciones

(no utilizará afirmaciones / condiciones previas para if-lets. Simplemente no parece correcto)

El uso de guardias también te ayuda a mejorar la claridad al evitar la pirámide de la fatalidad. Ver la respuesta de Nitin .


Guard crea una nueva variable

Hay una diferencia importante que creo que nadie ha explicado bien.

Tanto guard lety if let Separar la variable embargo

Con guard letusted está creando una nueva variable que existirá fuera de la elsedeclaración.

Si if letno está creando ninguna variable nueva, después de la instrucción else, solo ingresa el bloque de código si el opcional no es nulo. ¡La variable recién creada existe solo dentro del bloque de código, no después!

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

Para obtener más información if let, consulte: ¿Por qué la redeclaración de enlace opcional no crea un error?


Guardia requiere alcance salir

(También mencionado en la respuesta de Rob Napier):

DEBE haber guarddefinido dentro de una función. Su propósito principal es abortar / devolver / salir del alcance, si no se cumple una condición:

var str : String?

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

Para if letque no necesite tenerlo dentro de ninguna función:

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

guard vs if

Vale la pena señalar que es más apropiado para ver esta cuestión como guard letvs if lety guardvs if.

Un independiente ifno hace ningún desenvolvimiento, tampoco lo hace un independiente guard. Ver ejemplo a continuación. No sale temprano si un valor es nil. NO hay valores opcionales. Simplemente sale temprano si no se cumple una condición.

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]
}
Miel
fuente
46

Cuándo usar if-lety cuándo usar guardes a menudo una cuestión de estilo.

Supongamos que tiene func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intuna matriz opcional de elementos ( var optionalArray: [SomeType]?), y debe devolver 0si la matriz está nil(no establecida) o countsi la matriz tiene un valor (está establecida).

Puede implementarlo así usando if-let:

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

o así usando guard:

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

Los ejemplos son funcionalmente idénticos.

Donde guardrealmente brilla es cuando tienes una tarea como validar datos, y quieres que la función falle temprano si algo está mal.

En lugar de anidar un montón de if-lets a medida que se acerca a la finalización de la validación, la "ruta de éxito" y las opcionales ahora vinculadas con éxito están en el alcance principal del método, porque las rutas de falla ya han regresado.

divergio
fuente
30

Trataré de explicar la utilidad de las declaraciones de guardia con algún código (no optimizado).

Tiene una IU donde está validando campos de texto para el registro de usuarios con nombre, apellido, correo electrónico, teléfono y contraseña.

Si algún textField no contiene texto válido, debe hacer que ese campo sea FirstResponder.

Aquí está el código no optimizado:

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

Puede ver arriba, que todas las cadenas (firstNameString, lastNameString, etc.) son accesibles solo dentro del alcance de la instrucción if. por lo que crea esta "pirámide de la fatalidad" y tiene muchos problemas, incluida la legibilidad y la facilidad para mover las cosas (si se altera el orden de los campos, debe reescribir la mayor parte de este código)

Con la declaración de protección (en el código a continuación), puede ver que estas cadenas están disponibles fuera de {}y se utilizan, si todos los campos son válidos.

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

Si cambia el orden de los campos, simplemente mueva las líneas de código respectivas hacia arriba o hacia abajo, y estará listo para comenzar.

Esta es una explicación muy simple y un caso de uso. ¡Espero que esto ayude!

Nitin Alabur
fuente
14

Diferencia básica

Guardia deja

  1. Proceso temprano existe desde el alcance
  2. Requerir puntaje existente como retorno, lanzamiento, etc.
  3. Cree una nueva variable a la que se pueda acceder desde el ámbito.

si deja

  1. No se puede acceder al alcance.
  2. No es necesario devolver la declaración. Pero podemos escribir

NOTA: Ambos se usan para desenvolver la variable Opcional.

Kiran K
fuente
2

La explicación más clara que vi fue en la Guía de estilo Github Swift :

if agrega un nivel de profundidad:

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

guard no:

guard n.isNumber else {
    return
}
// Use n here
picos salvajes
fuente
2

Guardia

  • Una guarddeclaración se utiliza para transferir el control del programa fuera de un alcance si no se cumplen una o más condiciones.

  • El valor de cualquier condición en una guarddeclaración debe ser de tipo Bool o un tipo en puente Bool. La condición también puede ser una declaración vinculante opcional.

Una declaración de guardia tiene la siguiente forma:

guard condition else {
    //Generally return
}

si deja

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

Aprendí esto de Swift con Bob ...

Si no típico

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

Problemas con Else-If

  1. Soportes anidados
  2. Tiene que leer cada línea para detectar el mensaje de error.

Declaración de protección Un bloque de protección solo se ejecuta si la condición es falsa, y saldrá de la función mediante retorno. Si la condición es verdadera, Swift ignora el bloqueo. Proporciona una salida anticipada y menos paréntesis.

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

Desenvuelva los opcionales con otra opción

Una declaración de protección no solo es útil para reemplazar un bloque condicional típico con una declaración else-if, sino que también es excelente para desenvolver opciones al minimizar el número de paréntesis. Para comparar, primero comencemos a desenvolver múltiples opcionales con else-if. Primero, creemos tres opciones que se desenvolverán.

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

La peor pesadilla

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

El código anterior ciertamente funciona pero viola el principio DRY. Es atroz Vamos a desglosarlo. +

Ligeramente mejor El siguiente código es más legible que el anterior.

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

Desenvolver con Guardia Las declaraciones else-if pueden reemplazarse 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)
         }

Desenvuelva múltiples opcionales con Else-If Hasta ahora, ha estado desenvolviendo opcionales uno por uno. Swift nos permite desenvolver múltiples opciones a la vez. Si uno de ellos contiene nil, ejecutará el bloque 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")
  }
}

Tenga en cuenta que cuando desenvuelve múltiples opciones a la vez, no puede identificar cuál contiene cero

Desenvolver múltiples opcionales con Guardia Por supuesto, deberíamos usar guard sobre over-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
}
Ourang-Zeb Khan
fuente
¡por favor regrese y arregle el formato / sangría de su código!
pkamb