Échapper aux fermetures dans Swift

Je suis nouveau chez Swift et je lisais le manuel quand je suis tombé sur des fermetures échappantes. Je n'ai pas eu la description du manuel du tout. Quelqu'un pourrait-il m'expliquer ce que sont les fermetures échappantes en termes simples?

37
demandé sur PVitt 2016-09-15 09:13:32

4 réponses

Considérez cette classe:

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

someMethod affecte la fermeture passée à 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 je l'appelle anotherMethod, la fermeture { self.number = 10 }, seront stockés dans l'instance de A. Depuis self est capturé dans la fermeture, l'instance de A tiendra également une référence forte pour elle.

C'est essentiellement un exemple d'une fermeture échappée!

Vous Vous demandez probablement, "quoi? Alors où est la fermeture échappé, et à l'?"

, La fermeture s'échappe de la portée de la méthode, à la portée de la classe. Et il peut être appelé plus tard, même sur un autre fil! Cela pourrait causer des problèmes s'il n'est pas géré correctement.

Pour éviter d'échapper accidentellement aux fermetures et de causer des cycles de rétention et d'autres problèmes, utilisez l'attribut @noescape:

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

Maintenant, si vous essayez d'écrire self.closure = closure, il ne compile pas!

Mise à jour:

Dans Swift 3, Tous les paramètres de fermeture ne peuvent défaut. Vous devez ajouter l'attribut @escaping afin que la fermeture puisse échapper à la portée actuelle. Cela ajoute beaucoup plus de sécurité à votre code!

class A {
    var closure: (() -> Void)?
    func someMethod(closure: @escaping () -> Void) {
    }
}
64
répondu Sweeper 2017-08-11 13:03:46

Je vais d'une manière plus simple.

, Considérons cet exemple:

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

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

Considérons le même exemple avec une opération asynchorone:

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

L'exemple ci-dessus contient une fermeture d'échappement car l'appel de 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 ce qui précède cas vous pouvez facilement réaliser la fermeture se déplace à l'extérieur le corps de la fonction doit donc être une fermeture d'échappement.

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

35
répondu LC 웃 2017-05-23 09:35:09

Je trouve ce site très utile sur cette question Une explication Simple serait:

Si une fermeture est passée en argument à une fonction et qu'elle est invoquée après le retour de la fonction, la fermeture s'échappe.

En savoir plus sur le lien que je suis passé ci-dessus! :)

14
répondu stan 2016-12-08 15:47:20

Swift 4.1

À partir de la référence du langage: attributs du langage de programmation Swift (Swift 4.1)

Apple explique clairement l'attribut escaping.

Appliquez cet attribut au type d'un 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é à survivre à la durée de l'appel. Paramètres de type de fonction avec l'attribut type d'échappement exiger une utilisation explicite de soi. pour les propriétés ou méthodes. Pour un exemple d'utilisation de l'attribut évitement, voir Échapper Fermetures

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

La fonction someFunctionWithEscapingClosure(_:) prend une fermeture comme 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 obtiendrez une erreur de compilation.

On dit Qu'une fermeture échappe à une fonction lorsque la fermeture est passée en argument 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 du paramètre pour indiquer que la fermeture est autorisée à s'échapper.

2
répondu black_pearl 2018-06-04 08:02:54