closure - swift functions
Atributo @noescape en Swift 1.2 (3)
Hay un nuevo atributo en Swift 1.2 con parámetros de cierre en funciones, y como dice la documentación:
Esto indica que el parámetro solo se llama (o se pasa como un parámetro @ noescape en una llamada), lo que significa que no puede sobrevivir la vida útil de la llamada.
Según tengo entendido, antes de eso, podríamos usar
[weak self]
para no permitir que el cierre tenga una fuerte referencia a, por ejemplo, su clase, y self podría ser nulo o la instancia cuando se ejecuta el cierre, pero ahora,
@noescape
significa que el cierre nunca se ejecutará si se desinitaliza la clase.
¿Lo entiendo correctamente?
Y si estoy en lo correcto, ¿por qué usaría un cierre
@noescape
de una función regular, cuando se comportan de manera muy similar?
(En referencia a la respuesta de Michael Gray arriba).
No estoy seguro de si esto está específicamente documentado para Swift, o si incluso el compilador Swift lo aprovecha al máximo. Pero es un diseño de compilador estándar para asignar almacenamiento para una instancia en la pila si el compilador sabe que la función que se llama no intentará almacenar un puntero a esa instancia en el montón, y emitirá un error en tiempo de compilación si la función intenta hacerlo .
Esto es particularmente beneficioso cuando se pasan tipos de valores no escalares (como enumeraciones, estructuras, cierres) porque copiarlos es potencialmente mucho más costoso que simplemente pasar un puntero a la pila. La asignación de la instancia también es significativamente menos costosa (una instrucción versus llamar a malloc ()). Entonces, es una doble victoria si el compilador puede hacer esta optimización.
Una vez más, si una versión determinada del compilador Swift realmente tiene que ser declarada por el equipo de Swift, o tendría que leer el código fuente cuando lo abren. De la cita anterior sobre "optimización menor", parece que no es así o que el equipo de Swift lo considera "menor". Lo consideraría una optimización significativa.
Presumiblemente, el atributo está allí para que (al menos en el futuro) el compilador pueda realizar esta optimización.
Una forma de pensarlo es que CADA variable dentro del bloque @noescape no necesita ser Fuerte (no solo la propia).
También hay optimizaciones posibles ya que una vez que se asigna una variable que luego se envuelve en un bloque, no se puede desasignar normalmente al final de la función. Por lo tanto, debe asignarse en el montón y usar ARC para deconstruir. En Objective-C, debe usar la palabra clave "__block" para asegurarse de que la variable se cree de manera amigable con los bloques. Swift lo detectará automáticamente, por lo que la palabra clave no es necesaria, pero el costo es el mismo.
Si las variables se pasan a un bloque @nosecape, pueden ser variables de pila y no necesitan ARC para desasignar.
Las variables ahora ni siquiera necesitan ser variables débiles de referencia cero (que son más caras que los punteros inseguros) ya que se garantizará que estarán "vivas" durante la vida útil del bloque.
Todo esto da como resultado un código más rápido y óptimo. Y reduce la sobrecarga para usar bloques @autoclosure (que son muy útiles).
@noescape
se puede usar así:
func doIt(code: @noescape () -> ()) {
/* what we CAN */
// just call it
code()
// pass it to another function as another `@noescape` parameter
doItMore(code)
// capture it in another `@noescape` closure
doItMore {
code()
}
/* what we CANNOT do *****
// pass it as a non-`@noescape` parameter
dispatch_async(dispatch_get_main_queue(), code)
// store it
let _code:() -> () = code
// capture it in another non-`@noescape` closure
let __code = { code() }
*/
}
func doItMore(code: @noescape () -> ()) {}
Agregar
@noescape
garantiza que el cierre no se almacenará en algún lugar, se utilizará más adelante o se utilizará de forma asincrónica.
Desde el punto de vista de la persona que llama, no hay necesidad de preocuparse por la vida útil de las variables capturadas, ya que se utilizan dentro de la función llamada o no se utilizan en absoluto.
Y como beneficio adicional, podemos usar un
self
implícito, lo que nos salva de escribir
self.
.
func doIt(code: @noescape () -> ()) {
code()
}
class Bar {
var i = 0
func some() {
doIt {
println(i)
// ^ we don''t need `self.` anymore!
}
}
}
let bar = Bar()
bar.some() // -> outputs 0
Además, desde el punto de vista del compilador (como se documenta en las notas de la versión ):
Esto permite algunas optimizaciones de rendimiento menores.