47 votes

Échapper les fermetures en Swift

Je suis nouveau en Swift et je lisais le manuel quand j'ai rencontré les fermetures d'échappement. Je n'ai pas du tout compris la description du manuel. Est-ce que quelqu'un pourrait s'il vous plaît m'expliquer ce que sont les fermetures d'échappement en Swift en termes simples.

4 votes

Pour citer le manuel, « Une fermeture est dite échapper à une fonction lorsqu'elle est passée en argument à la fonction, mais est appelée après que la fonction retourne ». Donc, si la fermeture est appelée de manière synchrone, elle est non-échappante. Un exemple pourrait être une fermeture d'énumération, ou les méthodes fonctionnelles map, filter, etc. Si elle est appelée de manière asynchrone (c'est-à-dire plus tard), elle est échappante. L'exemple le plus courant de fermeture échappante serait le gestionnaire de fin d'une tâche asynchrone lente, comme une requête réseau.

1 votes

Si vous pensez que ma réponse répond à votre question, veuillez considérer l'accepter en cliquant sur cette coche.

73voto

Sweeper Points 1267

Considérez cette classe :

class A {
    var closure: (() -> Void)?
    func someMethod(closure: @escaping () -> Void) {
        self.closure = closure
    }
}

someMethod attribue la fermeture passée en paramètre à une propriété de la classe.

Maintenant voici une autre classe :

class B {
    var number = 0
    var a: A = A()
    func anotherMethod() {
        a.someMethod { self.number = 10 }
    }
}

Si j'appelle anotherMethod, la fermeture { self.number = 10 } sera stockée dans l'instance de A. Comme self est capture dans la fermeture, l'instance de A aura également une référence forte à cela.

C'est essentiellement un exemple de fermeture échappée !

Vous vous demandez probablement, "quoi ? Donc où la fermeture s'échappe-t-elle, et vers où ?"

La fermeture s'échappe du scope de la méthode, vers le scope de la classe. Et elle peut être appelée plus tard, même sur un autre thread ! Cela pourrait poser des problèmes s'il n'est pas géré correctement.

Par défaut, Swift n'autorise pas aux fermetures d'échapper. Vous devez ajouter @escaping au type de fermeture pour dire au compilateur "Veuillez permettre à cette fermeture de s'échapper". Si nous supprimons @escaping:

class A {
    var closure: (() -> Void)?
    func someMethod(closure: () -> Void) {
    }
}

et essayons d'écrire self.closure = closure, cela ne compile pas !

0 votes

Noescape est le comportement par défaut dans Swift 3 et obsolète. Ce que vous devez maintenant faire est d'utiliser escaping. func someMethod(closure: @escaping () -> Void)

1 votes

@bandejapaisa Merci pour l'information! Réponse modifiée!

0 votes

@bandejapaisa Vous dites que "@noescape" a été déprécié, mais je suis récemment tombé sur une situation qui me fait penser qu'il n'est que semi-déprécié. Merci de jeter un œil et de me dire si j'ai commis une erreur: stackoverflow.com/questions/41917413/…

48voto

LC 웃 Points 15362

Je vais d'une manière plus simple.

Considérez cet exemple:

func testFunctionWithNonescapingClosure(closure:() -> Void) {
        closure()
}

Ce qui précède est une fermeture non échappante car la fermeture est invoquée avant le retour de la méthode.

Considérez le même exemple avec une opération asynchrone:

func testFunctionWithEscapingClosure(closure:@escaping () -> Void) {
      DispatchQueue.main.async {
           closure()
      }
 }

L'exemple ci-dessus contient une fermeture échappante car l'invocation de la fermeture peut se produire après le retour de la fonction en raison de l'opération asynchrone.

 var completionHandlers: [() -> Void] = []
 func testFunctionWithEscapingClosure(closure: @escaping () -> Void) {
      completionHandlers.append(closure)
 }

Dans le cas ci-dessus, vous pouvez facilement constater que la fermeture se déplace en dehors du corps de la fonction, il est donc nécessaire qu'elle soit une fermeture échappante.

Les fermetures échappantes et non échappantes ont été ajoutées pour l'optimisation du compilateur dans Swift 3. Vous pouvez rechercher les avantages de la fermeture nonescaping.

0 votes

Il serait bon d'ajouter également à cette bonne réponse quels sont les avantages de la fermeture non échappante et des exemples de quand vous pourriez en avoir besoin.

17voto

stan Points 628

Je trouve ce site très utile sur ce sujet Une explication simple serait:

Si une fermeture est passée en argument à une fonction et qu'elle est invoquée après que la fonction ait retourné, la fermeture s'échappe.

Lire plus à partir du lien que j'ai passé ci-dessus! :)

10voto

DEEPAK KUMAR Points 23

Par défaut, les closures ne s'échappent pas. Pour une compréhension simple, vous pouvez considérer les closures non_escaping comme des fermetures locales (tout comme les variables locales) et les escaping comme des fermetures globales (tout comme les variables globales). Cela signifie qu'une fois que nous sortons du corps de la méthode, la portée de la fermeture non_escaping est perdue. Mais dans le cas de la fermeture escaping, la mémoire conserve la fermeture dans la mémoire.

*** Simplement, nous utilisons une fermeture escaping lorsque nous appelons la fermeture à l'intérieur de toute tâche asynchrone dans la méthode, ou que la méthode retourne avant d'appeler la fermeture.

Fermeture non_escaping: -

func add(num1: Int, num2: Int, completion: ((Int) -> (Void))) -> Int {
    DispatchQueue.global(qos: .background).async {
        print("Arrière-plan")
        complétion(num1 + num2) // Erreur : Utilisation de la fermeture de paramètre non-escaping 'completion' peut permettre à celle-ci de s'échapper
    }
    return num1
}

override func viewDidLoad() {
    super.viewDidLoad()
    let ans = add(num1: 12, num2: 22, completion: { (nombre) à l'intérieur de l'in
        print("Dans la fermeture")
        imprimer(nombre)
    })
    imprimer("Ans = \(ans)")
    initialSetup()
}

Étant donné qu'il s'agit d'une fermeture non_escaping, sa portée sera perdue une fois que nous sortons de la méthode 'add'. La complétion(num1 + num2) ne sera jamais appelée.

Fermeture escaping : -

func add(num1: Int, num2: Int, completion: @escaping((Int) -> (Void))) -> Int {
    DispatchQueue.global(qos: .background).async {
        print("Arrière-plan")
        complétion(num1 + num2)
    }
    return num1

Même si la méthode retourne (c'est-à-dire, que nous sortons de la portée de la méthode), la fermeture sera appelée.

6voto

black_pearl Points 176

Swift 4.1

De la référence de langage : Attributs du langage de programmation Swift (Swift 4.1)

Apple explique clairement l'attribut escaping.

Appliquez cet attribut à un type de paramètre dans une déclaration de méthode ou de fonction pour indiquer que la valeur du paramètre peut être stockée pour une exécution ultérieure. Cela signifie que la valeur est autorisée à survivre après l'appel. Les paramètres de type de fonction avec l'attribut de type escaping nécessitent une utilisation explicite de self. pour les propriétés ou les méthodes. Pour un exemple d'utilisation de l'attribut escaping, consultez Fermetures escaping

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

La fonction someFunctionWithEscapingClosure(_:) prend une fermeture en argument et l'ajoute à un tableau déclaré en dehors de la fonction. Si vous n'avez pas marqué le paramètre de cette fonction avec @escaping, vous obtiendriez une erreur de compilation.

Une fermeture est dite échapper à une fonction lorsque la fermeture est passée en argument à la fonction, mais est appelée après le retour de la fonction. Lorsque vous déclarez une fonction qui prend une fermeture comme l'un de ses paramètres, vous pouvez écrire @escaping avant le type de paramètre pour indiquer que la fermeture est autorisée à s'échapper.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X