parameter functions closure swift swift3 closures

functions - ¿Por qué los cierres requieren un ''yo'' explícito cuando no se escapan por defecto en Swift 3?



swift closure as function parameter (2)

He notado que en Swift 2.2, los cierres marcados como no @noescape con @noescape no requieren un self explícito. En Swift 3, todos los cierres no se escapan por defecto y ahora requiere que estén marcados con @escaping si desea que puedan escapar.

Dado que todos los cierres en Swift 3 por defecto no se escapan, ¿por qué requieren un self explícito?

final class SomeViewController: NSViewController { var someClosure: () -> () = { _ in } override func viewDidLoad() { super.viewDidLoad() someClosure = { view.layer = CALayer() // ERROR: Implicit use of `self` in closure; use `self.` to make capture semantics explicit } } }


En Swift 3, todos los cierres no se escapan por defecto

No, en Swift 3, solo los argumentos de la función de cierre (es decir, las entradas de función que son funciones en sí mismas) no se escapan por defecto (según SE-0103 ). Por ejemplo:

class A { let n = 5 var bar : () -> Void = {} func foo(_ closure: () -> Void) { bar = closure // As closure is non-escaping, it is illegal to store it. } func baz() { foo { // no explict ''self.'' required in order to capture n, // as foo''s closure argument is non-escaping, // therefore n is guaranteed to only be captured for the lifetime of foo(_:) print(n) } } }

Debido a que el closure en el ejemplo anterior es de no escape, está prohibido ser almacenado o capturado, limitando así su vida útil a la vida útil de la función foo(_:) . Por lo tanto, esto significa que se garantiza que cualquier valor que capture no permanecerá capturado después de que la función salga, lo que significa que no debe preocuparse por los problemas que pueden ocurrir con la captura, como los ciclos de retención.

Sin embargo, una propiedad almacenada de cierre (como la bar en el ejemplo anterior) se escapa por definición (sería absurdo marcarla con @noescape ) ya que su vida útil no se limita a una función determinada, es decir, (y por lo tanto todas sus variables capturadas) permanecerá en la memoria mientras la instancia dada permanezca en la memoria. Por lo tanto, esto puede llevar fácilmente a problemas como la retención de ciclos, por lo que debe usar un self. explícito self. para hacer explícita la semántica captadora.

De hecho, en este caso, su código de ejemplo creará un ciclo de viewDidLoad() cuando se viewDidLoad() a viewDidLoad() , ya que someClosure captura a self , y self referencia a someClosure , ya que es una propiedad almacenada.

Vale la pena señalar que como una extensión de la regla de "las propiedades de la función almacenada siempre se escapan", las funciones almacenadas en agregados (es decir, las estructuras y las enumeraciones con valores asociados) también se escapan, ya que no hay restricciones en lo que se hace con dichos agregados. Como lo señaló pandaren codemaster , esto actualmente incluye Optional , lo que significa que Optional<() -> Void> (aka. (() -> Void)? ) Siempre está escapando. Sin embargo, el compilador podría eventualmente convertir este caso en un caso especial para los parámetros de la función, dado que opcional ya está construido en un montón de magia de compilación.

Por supuesto, un lugar donde esperaría poder usar el atributo @noescape es en un cierre que es una variable local en una función. Dicho cierre tendría una vida útil predecible, siempre y cuando no se almacene fuera de la función, o se capture. Por ejemplo:

class A { let n = 5 func foo() { let f : @noescape () -> Void = { print(n) } f() } }

Desafortunadamente, como @noescape se está eliminando en Swift 3, esto no será posible (Lo interesante es que en Xcode 8 GM, es posible, pero produce una advertencia de desaprobación). Como dice Jon Shier , tendremos que esperar a que se vuelva a agregar al idioma, lo que puede o no suceder.


Los cierres almacenados se consideran escapar de forma predeterminada, incluso cuando en realidad no lo son. No hay forma de marcarlos para que no se escapen, por lo que estamos atrapados de esta manera hasta que agreguen @noescape al lenguaje, lo que pueden o no pueden hacer. Vea esta discusión en la lista de correo de swift-evolution.