Swift: guardia dejar vs si dejar

Resuelto lmiguelvargasf asked hace 9 años • 11 respuestas

He estado leyendo sobre 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 así sea, hacer algo con el valor desenvuelto.

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 es posible utilizarlo.

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

lmiguelvargasf avatar Aug 28 '15 01:08 lmiguelvargasf
Aceptado

if lety guard lettienen propósitos similares, pero distintos.

El caso "else" guarddebe salir del alcance actual. Generalmente eso significa que debe llamar returno cancelar el programa. guardse utiliza para proporcionar un retorno anticipado sin necesidad de anidar el 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 returno abortar, entonces debería usarlo guarden su lugar. Esto a menudo significa (al menos en mi experiencia), que en caso de duda, guardsuele ser la mejor respuesta. Pero hay muchas situaciones en las que if lettodavía es apropiado.

Rob Napier avatar Aug 27 '2015 18:08 Rob Napier

La guardia puede mejorar la claridad

Cuando usas guardia, tienes una expectativa mucho mayor de que la guardia tenga éxito y es algo importante que, si no tiene éxito, entonces solo quieras salir del alcance antes . Como si estuviera vigilando 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, le transmite al desarrollador de lectura que es más bien un 50-50. Pero si usas guard agregas claridad a tu código e implica que espero que esto funcione el 95% del tiempo... si alguna vez fallara, no sé por qué lo haría; es muy poco probable... pero entonces simplemente use esta imagen predeterminada o tal vez simplemente afirme con un mensaje significativo que describa lo que salió mal.

  • Evite guardlos protectores cuando crean efectos secundarios, los protectores deben usarse como un flujo natural . Evite las guardias cuando elselas cláusulas introduzcan efectos secundarios. Los guardias establecen las condiciones requeridas para que el código se ejecute correctamente, ofreciendo una salida temprana

  • Cuando realiza un cálculo significativo en la rama positiva, refactorice desde ifuna guarddeclaración y devuelva el valor alternativo en la elsecláusula.

De: Libro de estilo Swift 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 declaraciones de protección fallidas , esto simplemente mejora la legibilidad y deja 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 usarás 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 perdición. Vea la respuesta de Nitin .

Guard mantiene el código que maneja un requisito violado junto al requisito

Para ser claros, guardno siempre se trata de éxito versus fracaso. La forma más genérica de verlo es acerca del manejo de un requisito violado versus un código de proceso que no se viola.

ejemplo:

func getImage(completion: (image: Image)? -> Void) {
   guard cache["image1"] == false else {
      completion(cache["image1"]!)
   }
   downloadAndStore("image1") { image in 
       completion(image)
   }
}

En lo anterior, el requisito es que la imagen no esté presente en la memoria caché. Si la imagen está presente, entonces se viola nuestro requisito. Volvemos temprano. Como puede ver, también manejamos la ruta del código violado, justo al lado de su requisito, es decir, la estructura no es:

if requirement {
   . 
   . 
   ten lines of code
   . 
   . 
} else {
   handle requirement
}

Los Swift Docs sobre Control Flow explican la idea detrás de esto:

El uso de una declaración de guardia para los requisitos mejora la legibilidad de su código, en comparación con hacer la misma verificación con una declaración if.

  • Le permite escribir el código que normalmente se ejecuta sin envolverlo en un bloque else.
  • le permite mantener el código que maneja un requisito violado junto al requisito.

Guard evita el anidamiento creando una nueva variable en el alcance actual

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

Ambos guard lety if let desenvolver la variable sin embargo

Con guard letusted está creando una nueva variable que existirá en el alcance actual.

Con esto if letsolo estás creando una nueva variable dentro del bloque de código.

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 letconsulte: Por qué la redeclaración del enlace opcional no crea un error


El guardia requiere salir del alcance

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

DEBES haber guarddefinido dentro de una función. Su objetivo principal es cancelar/regresar/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)

Porque if letno es necesario tenerlo dentro de ninguna función:

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

guardvsif

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

Un standalone ifno desenvuelve nada, ni tampoco un standalone guard. Vea el 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]
}
mfaani avatar Sep 01 '2016 05:09 mfaani