Parámetro de cierre de escape opcional rápido

162

Dado:

typealias Action = () -> ()

var action: Action = { }

func doStuff(stuff: String, completion: @escaping Action) {
    print(stuff)
    action = completion
    completion()
}

func doStuffAgain() {
    print("again")
    action()
}

doStuff(stuff: "do stuff") { 
    print("swift 3!")
}

doStuffAgain()

¿Hay alguna forma de hacer el completionparámetro (y action) de tipo Action?y también mantener @escaping?

Cambiar el tipo da el siguiente error:

El atributo @escaping solo se aplica a los tipos de función

Al eliminar el @escapingatributo, el código se compila y se ejecuta, pero no parece ser correcto ya que el completioncierre está escapando del alcance de la función.

Lescai Ionel
fuente
21
"Al eliminar el @escapingatributo, el código se compila y se ejecuta". Eso se debe a que, como se describe en SR-2444 , se Action?está escapando de forma predeterminada. Por lo tanto, la eliminación @escapingal usar el cierre opcional logra lo que necesita.
Rob
los cierres de alias tipo escapan
Masih
Aquí hay un excelente artículo de Ole Begemann que describe por qué está sucediendo y algunas soluciones alternativas si desea que los parámetros opcionales sean @noescape.
Sentido

Respuestas:

122

Hay un informe SR-2552 que @escapingno reconoce el alias de tipo de función. Por eso el error @escaping attribute only applies to function types. puede solucionarlo expandiendo el tipo de función en la firma de la función:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
    print(stuff)
    action = completion
    completion?()
}

func doStuffAgain() {
    print("again")
    action?()
}

doStuff(stuff: "do stuff") {
    print("swift 3!")
}

doStuffAgain()

EDITAR 1 ::

En realidad, estaba bajo una versión beta de xcode 8 donde el error SR-2552 aún no se había resuelto. arreglando ese error, introduje uno nuevo (el que estás enfrentando) que todavía está abierto. ver SR-2444 .

La solución alternativa @Michael Ilseman señaló que una solución temporal es eliminar el @escapingatributo del tipo de función opcional, que mantiene la función como escape .

func doStuff(stuff: String, completion: Action?) {...}

EDITAR 2 ::

El SR-2444 se ha cerrado indicando explícitamente que los cierres en los parámetros posiciones no están escapando y ellos tienen que ser marcados con @escapinghacerlos escapar, pero los parámetros opcionales están escapando de forma implícita, ya que ((Int)->())?es una de sinónimos Optional<(Int)->()>, cierres opcionales están escapando.

Jans
fuente
55
Ahora recibiendo@escaping may only be applied to parameters of function type func doStuff(stuff: String, completion: (@escaping ()->())?) {
Lescai Ionel
1
a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping. ¿Puedes explicarlo mas a fondo? La semántica predeterminada en swift 3 no se escapa. Aunque se compila sin @escaping, me temo que causará problemas al ser tratado como no escapado. ¿No es eso cierto?
Pat Niemeyer
49
Después de leer más, veo que SR-2444 dice que todos los cierres opcionales se tratan como escape, lo cual es un error complementario :) Asumiré que cuando se solucione, la compilación nos advertirá que hagamos el cambio.
Pat Niemeyer
Quizás un poco fuera de tema; pero ¿cómo funciona esto @autoclosure? Uno obtiene el mismo error allí ...
Gee.E
funciona sin @escaping __ func doStuff (material: Cadena, finalización: (() -> ())?) {
Феннур Мезитов
226

de: lista de correo de usuarios rápidos

Básicamente, @escaping es válido solo en cierres en la posición del parámetro de función. La regla de noescape-by-default solo se aplica a estos cierres en la posición del parámetro de función, de lo contrario se están escapando. Los agregados, como las enumeraciones con valores asociados (por ejemplo, opcionales), tuplas, estructuras, etc., si tienen cierres, siguen las reglas predeterminadas para los cierres que no están en la posición del parámetro de función, es decir, están escapando.

Entonces, el parámetro de función opcional es @escaping por defecto.
@noeascape solo se aplica al parámetro de función de forma predeterminada.

Dmitry Coolerov
fuente
77
Creo que esto agrega la información más importante al tema, se debe aceptar la respuesta.
Damian Dudycz
La respuesta razonada. ¿Qué tan probable es que esto pueda cambiar?
GoldenJoe
2
Esto tiene sentido ya que técnicamente decir (()->Void)?es lo mismo que decir que tienes Optional<()->Void>y Optionalpara mantener la propiedad solo tendría que aceptar @escapingfunciones. Tercero, esta debería ser la respuesta aceptada. Gracias.
Dean Kelly
22

Me encontré con un problema similar porque mezclar @escapingy no @escapinges muy confuso, especialmente si necesita pasar los cierres.

Terminé asignando un valor predeterminado sin operación al parámetro de cierre a través de = { _ in }, lo que creo que tiene más sentido:

func doStuff(stuff: String = "do stuff",
        completion: @escaping (_ some: String) -> Void = { _ in }) {
     completion(stuff)
}

doStuff(stuff: "bla") {
    stuff in
    print(stuff)
}

doStuff() {
    stuff in
    print(stuff)
}
Freeman Man
fuente
Esto es limpio y bonito en la implementación.
Fred Faust
2
¿Qué sucede si quiero verificar si este bloque es nulo / vacío?
Vyachaslav Gerchicov
2
Bummer, esto no funciona para un método de protocolo. "Argumento predeterminado no permitido en un método de protocolo" (Xcode 8.3.2).
Mike Taverne
17

Lo hice funcionar en Swift 3 sin ninguna advertencia solo de esta manera:

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}
Igor
fuente
4

Lo importante que debes entender en el ejemplo es que si cambias Actional Action?cierre se escapa. Entonces, hagamos lo que propongas:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: Action?) {
    print(stuff)
    action = completion
    completion?()
}

Bien, ahora llamaremos doStuff:

class ViewController: UIViewController {
    var prop = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        doStuff(stuff: "do stuff") {
            print("swift 3!")
            print(prop) // error: Reference to property 'prop' in closure 
                        // requires explicit 'self.' to make capture semantics explicit
        }
    }
}

Bueno, ese requisito solo surge para escapar de los cierres. Entonces el cierre se está escapando. Es por eso que no lo marca como un escape, ya está escapando.

mate
fuente