Palabra clave de guardia de Swift

Resuelto David Snabel asked hace 9 años • 13 respuestas

Swift 2 introdujo la guardpalabra clave, que podría usarse para garantizar que varios datos estén configurados listos para funcionar. Un ejemplo que vi en este sitio web demuestra una función de envío:

func submitTapped() {
    guard username.text.characters.count > 0 else {
        return
    }

    print("All good")
}

Me pregunto si usar guardes diferente a hacerlo a la antigua usanza, usando una ifcondición. ¿Ofrece beneficios que no podría obtener con un simple cheque?

David Snabel avatar Jun 12 '15 04:06 David Snabel
Aceptado

Al leer este artículo , noté grandes beneficios al usar Guard.

Aquí puedes comparar el uso de guardia con un ejemplo:

Esta es la parte sin guardia:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
  1. Aquí estás poniendo tu código deseado dentro de todas las condiciones.

    Es posible que no vea inmediatamente un problema con esto, pero puede imaginar lo confuso que podría llegar a ser si estuviera anidado con numerosas condiciones que debían cumplirse antes de ejecutar sus declaraciones.

La forma de limpiar esto es hacer cada una de las comprobaciones primero y salir si no se cumple alguna. Esto permite comprender fácilmente qué condiciones harán que esta función salga.

Pero ahora podemos usar guard y podemos ver que es posible resolver algunos problemas:

func fooGuard(x: Int?) {
    guard let x = x where x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
    x.description
}
  1. Comprobando la condición que desea, no la que no desea. Esto nuevamente es similar a una afirmación. Si no se cumple la condición, se ejecuta la instrucción else de guard, que sale de la función.
  2. Si se cumple la condición, la variable opcional aquí se desenvuelve automáticamente dentro del alcance en el que se llamó la declaración de protección; en este caso, la función fooGuard(_:).
  3. Usted está comprobando casos malos con anticipación, lo que hace que su función sea más legible y más fácil de mantener.

Este mismo patrón también se aplica a los valores no opcionales:

func fooNonOptionalGood(x: Int) {
    guard x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

func fooNonOptionalBad(x: Int) {
    if x <= 0 {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

Si aún tienes dudas puedes leer el artículo completo: Declaración de Swift Guard.

Terminando

Y finalmente, leyendo y probando descubrí que si usas guard para desenvolver cualquier opcional,

esos valores desenvueltos permanecen disponibles para que usted los use en el resto de su bloque de código

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")

Aquí el valor desenvuelto solo estaría disponible dentro del bloque if

if let unwrappedName = userName {
    print("Your username is \(unwrappedName)")
} else {
    return
}

// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")
Jorge Casariego avatar Jun 16 '2015 12:06 Jorge Casariego

A diferencia de if, guardcrea la variable a la que se puede acceder desde fuera de su bloque. Es útil desenvolver muchos Optionals.

takebayashi avatar Jun 11 '2015 21:06 takebayashi

En realidad, existen dos grandes beneficios guard. Uno es evitar la pirámide de la perdición, como otros han mencionado: muchas if letdeclaraciones molestas anidadas unas dentro de otras que se mueven cada vez más hacia la derecha.

El otro beneficio es que a menudo la lógica que desea implementar es más " if not let" que " if let { } else".

Aquí hay un ejemplo: suponga que desea implementar accumulate: un cruce entre mapy reducedonde le devuelve una serie de reducciones en ejecución . Aquí está con guard:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
        // if there are no elements, I just want to bail out and
        // return an empty array
        guard var running = self.first else { return [] }

        // running will now be an unwrapped non-optional
        var result = [running]

        // dropFirst is safe because the collection
        // must have at least one element at this point
        for x in dropFirst(self) {
            running = combine(running, x)
            result.append(running)
        }
        return result
    }

}


let a = [1,2,3].accumulate(+)  // [1,3,6]
let b = [Int]().accumulate(+)  // []

¿Cómo lo escribirías sin protección, pero aún usándolo firstdevuelve un opcional? Algo como esto:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {

        if var running = self.first  {
            var result = [running]

            for x in dropFirst(self) {
                running = combine(running, x)
                result.append(running)
            }
            return result
        }
        else {
            return []
        }
    }

}

El anidamiento adicional es molesto, pero además no es tan lógico tener el ify el elsetan separados. Es mucho más legible tener la salida anticipada para el caso vacío y luego continuar con el resto de la función como si eso no fuera una posibilidad.

Airspeed Velocity avatar Jun 11 '2015 22:06 Airspeed Velocity

Cuando se cumple una condición usando, guardexpone las variables declaradas dentro del guardbloque al resto del bloque de código, llevándolas a su alcance. Lo cual, como se indicó anteriormente, sin duda será útil con if letdeclaraciones anidadas.

Tenga en cuenta que guard requiere una devolución o un lanzamiento en su declaración else.

Analizando JSON con Guard

A continuación se muestra un ejemplo de cómo se puede analizar un objeto JSON usando guard en lugar de if-let. Este es un extracto de una entrada de blog que incluye un archivo de juegos que puede encontrar aquí:

Cómo usar Guard en Swift 2 para analizar JSON

func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {

    guard let firstname = data["First"] as? String  else {
        return Developer() // we could return a nil Developer()
    }

    guard let lastname = data["Last"] as? String else {
        throw ParseError.BadName // or we could throw a custom exception and handle the error
    }

    guard let website = data["WebSite"] as? String else {
        throw ParseError.BadName
    }

    guard let iosDev = data["iosDeveloper"] as? Bool else {
        throw ParseError.BadName
    }



    return Developer(first: firstname, last: lastname, site: website, ios: iosDev)

}

descargar parque infantil: parque infantil de guardia

Más información:

Aquí hay un extracto de la Guía del lenguaje de programación Swift:

Si se cumple la condición de la declaración de guardia, la ejecución del código continúa después de la llave de cierre de la declaración de guardia. Cualquier variable o constante a la que se le asignaron valores mediante un enlace opcional como parte de la condición está disponible para el resto del bloque de código en el que aparece la declaración de protección.

Si no se cumple esa condición, se ejecuta el código dentro de la rama else. Esa rama debe transferir el control para salir del bloque de código en el que aparece esa declaración de protección. Puede hacer esto con una declaración de transferencia de control como return, break o continue, o puede llamar a una función o método que no regresa, como como error fatal().

Dan Beaulieu avatar Jun 11 '2015 21:06 Dan Beaulieu