programming - swift variables
Swift: guardia vs si se deja (8)
He estado leyendo acerca de los 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 lo haga, haga algo con el valor sin envolver.
Sin embargo, he visto que en Swift 2.0 la
guard
palabras clave se usa principalmente.
Me pregunto
if let
se ha eliminado de Swift 2.0 o si todavía se puede usar.
¿Debo cambiar mis programas que contienen
if let
guard
?
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 temprano . 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 luego use esta imagen predeterminada en su lugar o tal vez simplemente afirme con un mensaje significativo que describa lo que salió mal!
Evite los
guard
cuando crean efectos secundarios, los guardias deben usarse como un flujo natural . Evite los guardias cuandoelse
cláusulas introducen efectos secundarios. Los guardias establecen las condiciones requeridas para que el código se ejecute correctamente, ofreciendo una salida anticipadaCuando realiza un cálculo significativo en la rama positiva, refactorice desde
if
a una declaración deguard
y devuelva el valor de recuperación en la cláusulaelse
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-let
s. Simplemente no parece correcto)
Usar 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.
Sin embargo, ambos
guard
y
if let
desenvolver
la variable
Con
guard
estás
creando
una nueva variable que
existirá
fuera de la instrucción
else
.
if let
crear
ninguna nueva variable, 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:
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 sobre
if let
ver:
¿Por qué la redeclaración de enlace opcional no crea un error?
Guardia requiere salir del alcance
(También mencionado en la respuesta de Rob Napier):
DEBE tener un
guard
definido
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)
Por
if let
te
if let
no necesitas tenerlo dentro de ninguna función:
var str : String?
if let blogName1 = str {
print(blogName1) // You don''t get any errors!
}
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
- Soportes anidados
- 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 soportes.
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 se pueden reemplazar con guardia.
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 opciones 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
}
Cuándo usar
if-let
y cuándo usar
guard
es a menudo una cuestión de estilo.
Supongamos que tiene
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
y una matriz opcional de elementos (
var optionalArray: [SomeType]?
), Y necesita devolver
0
si la matriz es
nil
(no establecida ) o el
count
si la matriz tiene un valor (se establece).
Puede implementarlo de esta manera 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 realmente brilla la
guard
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-let
s a medida que se acerca a finalizar la validación, la "ruta de éxito" y las opciones ahora vinculadas con éxito están en el alcance principal del método, porque las rutas de falla ya han regresado.
Diferencia básica
Guardia deja
- Proceso temprano existe desde el alcance
- Requerir puntaje existente como retorno, lanzamiento, etc.
- Cree una nueva variable a la que se pueda acceder desde el ámbito.
si deja
- No se puede acceder al alcance.
- No es necesario devolver la declaración. Pero podemos escribir
NOTA: Ambos se utilizan para desenvolver la variable Opcional.
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
Trataré de explicar la utilidad de las declaraciones de guardia con algún código (no optimizado).
Tiene una interfaz de usuario 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.) solo son accesibles 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!
Se utiliza una declaración de guardia 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 declaración de protección debe ser de tipo Bool o un tipo puenteado a 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
}
- También popular como encuadernación opcional
- Para acceder al objeto opcional usamos if let
if let roomCount = optionalValue {
print("roomCount available")
} else {
print("roomCount is nil")
}
if let
and
guard let
sirve para propósitos similares pero distintos.
El caso de
guard
"else" debe salir del alcance actual.
En general, eso significa que debe llamar a
return
o abortar el programa.
guard
se usa para proporcionar un retorno temprano sin requerir el anidamiento del resto de la función.
if let
anidar su alcance, y no requiere nada especial de él.
Puede
return
o no.
En general, si el bloque
if-let
iba a ser el resto de la función, o su cláusula
else
tendría un
return
o un aborto, entonces debería usar
guard
lugar.
Esto a menudo significa (al menos en mi experiencia), en caso de duda, la
guard
es la mejor respuesta.
Pero hay muchas situaciones en las que
if let
todavía es apropiado.