closures - functions - Cómo usar Swift @autoclosure
swift functions (5)
Aquí hay un ejemplo práctico: mi anulación de print
(esta es Swift 3):
func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "/n") {
#if DEBUG
Swift.print(item(), separator:separator, terminator: terminator)
#endif
}
Cuando dices print(myExpensiveFunction())
, mi anulación de print
eclipsa la print
de Swift y se print(myExpensiveFunction())
. myExpensiveFunction()
se envuelve en un cierre y no se evalúa . Si estamos en modo Liberación, nunca será evaluado, porque no se llamará al item()
. Por lo tanto, tenemos una versión de print
que no evalúa sus argumentos en el modo de lanzamiento.
Me di cuenta al escribir una assert
en Swift que el primer valor se escribe como
@autoclosure() -> Bool
con un método sobrecargado para devolver un valor T
genérico, para probar la existencia a través del protocol
LogicValue
.
Sin embargo, se atiene estrictamente a la pregunta que nos ocupa. Parece querer un @autoclosure
que devuelva un Bool
.
Escribir un cierre real que no tome parámetros y devuelva un Bool no funciona, quiere que llame al cierre para hacer que se compile, así:
assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__)
Sin embargo, simplemente pasar un Bool funciona:
assert(false, "No user has been set", file: __FILE__, line: __LINE__)
¿Entonces qué está pasando? ¿Qué es @autoclosure
?
Editar: @auto_closure
se renombró @autoclosure
Considere una función que toma un argumento, un cierre simple que no tiene argumento:
func f(pred: () -> Bool) {
if pred() {
print("It''s true")
}
}
Para llamar a esta función, tenemos que pasar un cierre
f(pred: {2 > 1})
// "It''s true"
Si omitimos las llaves, estamos pasando una expresión y eso es un error:
f(pred: 2 > 1)
// error: ''>'' produces ''Bool'', not the expected contextual result type ''() -> Bool''
@autoclosure
crea un cierre automático alrededor de la expresión. Entonces, cuando la persona que llama escribe una expresión como 2 > 1
, se ajusta automáticamente a un cierre para convertirse en {2 > 1}
antes de pasarla a f
. Entonces, si aplicamos esto a la función f
:
func f(pred: @autoclosure () -> Bool) {
if pred() {
print("It''s true")
}
}
f(pred: 2 > 1)
// It''s true
Por lo tanto, funciona con solo una expresión sin la necesidad de envolverlo en un cierre.
Descripción de auto_closure de los documentos:
Puede aplicar el atributo auto_closure a un tipo de función que tenga un tipo de parámetro de () y que devuelva el tipo de una expresión (consulte Atributos de tipo). Una función de cierre automático captura un cierre implícito sobre la expresión especificada, en lugar de la expresión misma. El siguiente ejemplo usa el atributo auto_closure para definir una función de afirmación muy simple:
Y aquí está el ejemplo que la manzana usa junto con él.
func simpleAssert(condition: @auto_closure () -> Bool, message: String) {
if !condition() {
println(message)
}
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn''t an even number.")
Básicamente, lo que significa es pasar una expresión booleana como primer argumento en lugar de un cierre y automáticamente crea un cierre para usted. Es por eso que puede pasar falso en el método porque es una expresión booleana, pero no puede pasar un cierre.
Es solo una manera de deshacerse de las llaves en una llamada de cierre, ejemplo simple:
let nonAutoClosure = { (arg1: () -> Bool) -> Void in }
let non = nonAutoClosure( { 2 > 1} )
let autoClosure = { (arg1: @autoclosure () -> Bool) -> Void in }
var auto = autoClosure( 2 > 1 ) // notice curly braces omitted
Esto muestra un caso útil de @autoclosure
https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/
Ahora, la expresión condicional pasada como el primer parámetro a hasta se envolverá automáticamente en una expresión de cierre y se podrá invocar cada vez que se repita el ciclo
func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) {
while !pred() {
block()
}
}
// doSomething until condition becomes true
until(condition) {
doSomething()
}