web-dev-qa-db-fra.com

Swift 3: l'utilisation de fermeture du paramètre non d'échappement peut lui permettre de s'échapper

J'ai la fonction suivante où j'ai un gestionnaire d'achèvement mais j'obtiens cette erreur:

Closure use of non-escaping parameter may allow it to escape

Voici mon code:

func makeRequestcompletion(completion:(_ response:Data, _ error:NSError)->Void)  {
    let urlString = URL(string: "http://someUrl.com")
    if let url = urlString {
        let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
            completion(data, error) // <-- here is I'm getting the error
        })
    task.resume()
    }
}

enter image description here L'un de vous sait pourquoi je reçois cette erreur?

J'apprécierai vraiment votre aide

14
user2924482

Il semble que vous devez définir explicitement que la fermeture peut s'échapper.

À partir de Apple Developer docs ,

Une fermeture est censée échapper à une fonction lorsqu'elle est passée en argument à la fonction, mais elle 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 peut s'échapper.

TLDR; Ajouter le @escaping mot-clé après la variable d'achèvement:

func makeRequestcompletion(completion: @escaping (_ response:Data, _ error:NSError)->Void)  {
    let urlString = URL(string: "http://someUrl.com")
    if let url = urlString {
        let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
            completion(data, error) // <-- here is I'm getting the error
        })
        task.resume()
    }
}
8
Mark Barrasso

Une fermeture "d'échappement" est une fermeture qui peut survivre à l'étendue dans laquelle elle a été créée. Les fermetures d'échappement nécessitent une attention particulière concernant le comptage des références et la gestion de la mémoire et peuvent être plus difficiles à optimiser.

Avant Swift 3, la valeur par défaut des fermetures était de supposer qu'elles s'échappaient. Cela signifiait que les développeurs devaient identifier spécifiquement les fermetures connues pas pour s'échapper afin de permettre le compilateur pour faire des optimisations. La communauté a constaté qu'en fait, le compilateur pouvait facilement trouver par lui-même si une fermeture s'échappe ou non, et a décidé qu'une approche agressive de l'échappement pourrait entraîner un code plus rapide. Le résultat est que les fermetures sont maintenant supposé ne pas s'échapper, et vous devez signaler les fermetures qui s'échappent avec le @escaping attribut.

Dans votre cas, la fermeture qui URLSession.shared.dataTask accepte est lui-même une fermeture d'échappement, donc si vous utilisez une fermeture à l'intérieur, il doit également être marqué @escaping.

3
zneak

@escaping est contagieux pour toutes les méthodes d'appel, et le compilateur détermine quand vous devez l'inclure.

Considérez cet exemple (qui compile):

dispatchSometime( { print("Oh yeah") })

func dispatchSometime(_ block: ()->()) {
    dispatchNow(block)
}

func dispatchNow(_ block: ()->()) {
    block()
}

Cependant, cet exemple modifié produit deux erreurs de type non-escaping parameter may allow it to escape:

dispatchSometime( { print("Oh yeah") })

func dispatchSometime(_ block: ()->()) {
    dispatchLater(block)
}

func dispatchLater(_ block: ()->()) {
    DispatchQueue.main.async(execute: block)
}

La répartition sur main signifie que la méthode dispatchLater a besoin de @escaping, et une fois que vous avez ajouté cela, la méthode dispatchSometimeaussi nécessite @escaping pour l'exemple à compiler.

dispatchSometime( { print("Oh yeah") })

func dispatchSometime(_ block: @escaping ()->()) {
    dispatchLater(block)
}

func dispatchLater(_ block: @escaping ()->()) {
    DispatchQueue.main.async(execute: block)
}

Cependant, le plat à emporter est juste:

  • Continuez à ajouter @escaping jusqu'à la chaîne d'appel jusqu'à ce que le compilateur cesse de se plaindre.
  • Le mot-clé ne change rien: c'est un avertissement qui dit, essentiellement, "faites attention à utiliser weak avec les variables capturées car elles peuvent être conservées avec le bloc lui-même."

Implications

Le cas vraiment amusant avec cela est où vous devez ajuster plusieurs méthodes pour inclure le @escaping mot clé, ce qui empêche le compilateur de se plaindre. Cependant, si ces méthodes sont réellement conformes à un protocole, les méthodes de ce protocole doivent également obtenir le @escaping mot clé, qui infecte également tous les autres conformants au protocole. Amusement!

1
Dan Rosenstark