web-dev-qa-db-fra.com

Swift 3 paramètre de fermeture d'échappement optionnel

Donné:

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()

Est-il possible de créer le paramètre completion (et action) de type Action? et de conserver également @escaping?

Changer le type donne l'erreur suivante:

error: @escaping attribute only applies to function types

En supprimant l'attribut @escaping, le code est compilé et exécuté, mais ne semble pas être correct car la fermeture de completion échappe à la portée de la fonction.

143
Lescai Ionel

Un SR-2552 indique que @escaping ne reconnaît pas l'alias de type de fonction. c'est pourquoi l'erreur @escaping attribute only applies to function types. vous pouvez contourner le problème en développant le type de fonction dans la signature de fonction:

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()

EDIT 1: :

J'étais actuellement sous une version bêta de xcode 8 où le bogue SR-2552 n'était pas encore résolu. en corrigeant ce bogue, en a introduit un nouveau (celui auquel vous faites face) qui est toujours ouvert. voir SR-2444 .

La solution de contournement @ Michael Ilseman indiquée comme solution temporaire consiste à supprimer l'attribut @escaping du type de fonction facultatif, qui conserve la fonction en tant que s'échapper .

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

EDIT 2: :

Le SR-2444 a été fermé indiquant explicitement que les fermetures dans les positions de paramètres ne s'échappent pas et nécessitent leur marquage avec @escaping pour les échapper, mais les paramètres facultatifs échappent implicitement , puisque ((Int)->())? est un synonyme de Optional<(Int)->()> , les fermetures optionnelles s'échappent.

104
Jans

à partir de: liste de diffusion Swift-users

Fondamentalement, @escaping est valide uniquement sur les fermetures en position de paramètre de fonction. La règle noescape-by-default ne s'applique qu'à ces fermetures à la position du paramètre de la fonction, sinon elles s'échappent. Les agrégats, tels que les énumérations avec les valeurs associées (par exemple, facultative), les nuplets, les structures, etc., s'ils ont des fermetures, respectent les règles par défaut pour les fermetures qui ne sont pas à la position du paramètre de fonction, c'est-à-dire qu'elles s'échappent.

Donc, le paramètre de fonction facultatif est @escaping par défaut.
@ noeascape s'applique uniquement au paramètre de fonction par défaut.

189
Dmitry Coolerov

je rencontre un problème similaire et parce que mélanger @ évacuer et non @ évacuer est très déroutant, surtout si vous devez faire passer les fermetures. Je me retrouve avec des paramètres par défaut (ce qui me semble plus logique)

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)
}
16
Freeman Man

Je l'ai fait fonctionner dans Swift 3 sans aucun avertissement uniquement de cette façon:

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

La chose importante à comprendre dans l'exemple est que si vous changez Action en Action?, la fermeture s'échappe . Alors, faisons ce que vous proposez:

typealias Action = () -> ()

var action: Action? = { }

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

Bon, maintenant nous allons appeler 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
        }
    }
}

Eh bien, cette exigence ne se pose que pour échapper aux fermetures. Donc, la fermeture s'échappe. C'est pourquoi vous ne ( ne marquez pas cela s'échappant - cela s'échappe déjà.

1
matt