Swift: guardia dejar vs si dejar
He estado leyendo sobre Opcionales en Swift y he visto ejemplos en los que if let
se 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 let
se usa principalmente. Me pregunto si if let
se ha eliminado de Swift 2.0 o si todavía es posible utilizarlo.
¿Debo cambiar mis programas que contienen if let
a guard let
?
if let
y guard let
tienen propósitos similares, pero distintos.
El caso "else" guard
debe salir del alcance actual. Generalmente eso significa que debe llamar return
o cancelar el programa. guard
se utiliza para proporcionar un retorno anticipado sin necesidad de anidar el resto de la función.
if let
anida su alcance y no requiere nada especial de él. Puede return
o no.
En general, si el if-let
bloque iba a ser el resto de la función, o su else
cláusula tendría return
o abortar, entonces debería usarlo guard
en su lugar. Esto a menudo significa (al menos en mi experiencia), que en caso de duda, guard
suele ser la mejor respuesta. Pero hay muchas situaciones en las que if let
todavía es apropiado.
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
guard
los protectores cuando crean efectos secundarios, los protectores deben usarse como un flujo natural . Evite las guardias cuandoelse
las cláusulas introduzcan efectos secundarios. Los guardias establecen las condiciones requeridas para que el código se ejecute correctamente, ofreciendo una salida tempranaCuando realiza un cálculo significativo en la rama positiva, refactorice desde
if
unaguard
declaración y devuelva el valor alternativo en laelse
clá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-let
s. 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, guard
no 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 let
y if let
desenvolver la variable sin embargo
Con guard let
usted está creando una nueva variable que existirá en el alcance actual.
Con esto if let
solo 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 let
consulte: 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 guard
definido 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 let
no es necesario tenerlo dentro de ninguna función:
var str : String?
if let blogName1 = str {
print(blogName1) // You don't get any errors!
}
guard
vsif
Vale la pena señalar que es más apropiado ver esta pregunta como guard let
vs if let
y guard
vs.if
Un standalone if
no 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]
}