web-dev-qa-db-fra.com

Swift @ Escaping and Completion Handler

J'essaie de comprendre plus précisément la «fermeture» de Swift.

Mais @escaping et Completion Handler sont trop difficiles à comprendre

J'ai consulté de nombreuses publications et documents officiels de Swift, mais j’ai pensé que cela n’était toujours pas suffisant.

Ceci est l'exemple de code des documents officiels 

var completionHandlers: [()->Void] = []

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

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

class SomeClass{
    var x:Int = 10
    func doSomething(){
        someFunctionWithEscapingClosure {
            self.x = 100
            //not excute yet
        }
        someFunctionWithNoneescapingClosure {
            x = 200
        }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)

completionHandlers.first?() 
print(instance.x)

J'ai entendu dire qu'il y a deux manières et raisons d'utiliser @escaping 

Le premier concerne le stockage d'une fermeture, le second est destiné à une utilisation asynchrone.

Voici mes questions :

Premièrement, si doSomething est exécuté, someFunctionWithEscapingClosure sera exécuté avec le paramètre de fermeture et cette fermeture sera enregistrée dans un tableau de variables globales. 

Je pense que la fermeture est {self.x = 100}

Comment self dans {self.x = 100} qui a été enregistré dans la variable globale completionHandlers peut-il se connecter à instance cet objet de SomeClass?

Deuxièmement, je comprends someFunctionWithEscapingClosure comme ceci.

Pour stocker la variable de fermeture locale completionHandler dans la variable globale 'completionHandlerswe using @ escaping`, mot-clé! 

sans @escaping mot-clé someFunctionWithEscapingClosure renvoie, la variable locale completionHandler sera supprimée de la mémoire

@escaping est de garder cette fermeture en mémoire

Est-ce correct?

Enfin, je m'interroge sur l'existence de cette grammaire.

C'est peut-être une question très rudimentaire.

Si nous voulons qu'une fonction soit exécutée après une fonction spécifique. Pourquoi n'appelons-nous pas une fonction après un appel de fonction spécifique?

Quelles sont les différences entre l'utilisation du modèle ci-dessus et l'utilisation d'une fonction de rappel d'échappement? 

65
Dongkun Lee

Tout d'abord, je veux dire "Très bonne question :)"

Completion Handler:

Supposons que l'utilisateur met à jour une application tout en l'utilisant. Vous voulez absolument informer l'utilisateur quand c'est fait. Vous voudrez peut-être faire apparaître une boîte qui dit: «Félicitations, maintenant, vous pourrez en profiter pleinement!»

Alors, comment exécutez-vous un bloc de code uniquement une fois le téléchargement terminé? De plus, comment n'animez-vous certains objets qu'après le déplacement d'un contrôleur de vue vers le suivant? Eh bien, nous allons découvrir comment en concevoir un comme un patron ... Sur la base de ma liste de vocabulaire expansive, les gestionnaires d'achèvement représentent

Fait des choses quand les choses ont été faites

Pour plus de détails, veuillez visiter cet article de blog .

Ce lien me donne une clarté complète sur les gestionnaires de complétion (du point de vue du développeur, il définit exactement ce que nous devons comprendre).

@ fermant les fermetures:

Lorsque vous passez la clôture dans les arguments d’une fonction, utilisez-la une fois que le corps de la fonction est exécuté et renvoie le compilateur. Lorsque la fonction se termine, l'étendue de la fermeture passée existe et a une existence en mémoire, jusqu'à l'exécution de la fermeture.

Il y a plusieurs façons d'échapper à la fermeture dans la fonction contenant:

  • Stockage: Lorsque vous devez stocker la fermeture dans la variable globale, la propriété ou tout autre stockage existant dans la mémoire en retard de la fonction appelante est exécuté et renvoie le compilateur.

  • Exécution asynchrone: lorsque vous exécutez la fermeture de manière asynchrone dans la file d’expédition, celle-ci conservera la fermeture en mémoire pour vous et pourra être utilisée ultérieurement. Dans ce cas, vous ne savez pas quand la fermeture sera exécutée.

Lorsque vous essayez d'utiliser la fermeture dans ces scénarios, le compilateur Swift affiche l'erreur:

error screenshot

Pour plus de clarté sur ce sujet, vous pouvez consulter ce post sur Medium

J'espère que vous obtiendrez une bonne compréhension de ce lien.

Si vous avez encore des questions (mais assurez-vous de lire ce lien ligne par ligne d’abord, c’est très bien expliqué), n'hésitez pas à partager votre commentaire.

Merci, continuez à poster si cette réponse doit être mise à jour

86
Shobhakar Tiwari

Voici une petite classe d’exemples que j’utilise pour me rappeler le fonctionnement de @scaping.

class EscapingExamples: NSObject {

    var closure: (() -> Void)?

    func storageExample(with completion: (() -> Void)) {
        //This will produce a compile time error because `closure` is outside the scope of this
        //function - it's a class-instance level variable - and so it could be called by any other method at
        //any time, even after this function has completed.  We need to tell `completion` that it may remain in memory, i.e. `escape` the scope of this
        //function.
        closure = completion
        //Run some function that may call `closure` at some point, but not necessary for the error to show up.
        //runOperation()
    }

    func asyncExample(with completion: (() -> Void)) {
        //This will produce a compile time error because the completion closure may be called at any time
        //due to the async nature of the call which preceeds/encloses it.  We need to tell `completion` that it should
        //stay in memory, i.e.`escape` the scope of this function.
        DispatchQueue.global().async {
            completion()
        }
    }

    func asyncExample2(with completion: (() -> Void)) {
        //The same as the above method - the compiler sees the `@escaping` nature of the
        //closure required by `anotherThing()` and tells us we need to allow our own completion
        //closure to be @escaping too.  `anotherThing`'s completion block will be retained in memory until
        //it is executed, so our completion closure must explicitely do the same.
        runAsyncTask {
            completion()
        }
    }





    func runAsyncTask(completion: @escaping (() -> Void)) {
        DispatchQueue.global().async {
            completion()
        }
    }

}
16
JamesK

Fonction

Vous définissez une fonction avec le mot clé func. Les fonctions peuvent prendre plusieurs paramètres et ne renvoyer aucun, un ou plusieurs paramètres

Fermeture

Les fermetures sont des blocs de fonctionnalités autonomes qui peuvent être échangés et utilisés dans votre code. Les fermetures dans Swift sont similaires aux blocs en C et Objective-C et aux lambdas dans d'autres langages de programmation.

Les fonctions et les fermetures sont des types de première classe dans Swift:

  • assigner une fonction/fermeture à une variable locale
  • passer une fonction/fermeture en argument
  • renvoyer une fonction/fermeture

Fermeture échappée vs fermeture non échappée

  • non-escaping closure@noescape est une fermeture appelée dans la fonction dans laquelle elle a été transmise, c'est-à-dire avant son retour.

  • escaping closure@escaping est une fermeture appelée après la fonction à laquelle elle a été transmise. En d'autres termes, il survit à la fonction à laquelle il a été transmis. Les cas d'utilisation courants pour cela sont:

    • Appels asynchrones; la mise en réseau.
    • Fonctions stockées sous forme de variables; penser actions et rappels fournis.
    • Planification des tâches dans une file d'attente de distribution.

* @noescape était un attribut dans Swift2. Ceci est déconseillé à partir de Swift3. L'attribut @noescape est appliqué par défaut dans Swift3. Comme les fermetures sont par défaut non fugitives dans Swift3, elles doivent être marquées comme telles. Et l'attribut @escaping nous permet de le faire.

Gestionnaire d'achèvement

Un bon exemple de escaping closure est un completion handler. De nombreuses fonctions qui démarrent une opération asynchrone prennent un argument de fermeture en tant que gestionnaire d'achèvement. La fonction revient après le début de l'opération, mais la fermeture n'est pas appelée tant que l'opération n'est pas terminée. La fermeture doit s'échapper pour être appelée plus tard.

Lire la suite ici - Poste moyen , Poste moyen , docs

1
yoAlex5
import UIKit
import Alamofire

// Modèle

class ShortlistCountResponse : Decodable {
    var response : String?
    var data : ShortlistcountData?

}
class ShortlistcountData : Decodable {

    var totalpropFavcount : Int?
    var totalprojFavcount : Int?

}

// Définition de classe générique ......

static func fetchGenericData<T: Decodable>(urlString: String,params : [String:Any], completion: @escaping (T) -> ()) {
        let url = urlString
        let headers = ["Content-Type": "application/x-www-form-urlencoded", "Accept":"application/json"]
        Alamofire.request(url, method: .post, parameters:params, encoding: URLEncoding.default, headers: headers).responseJSON { response in
            print(response.request?.urlRequest ?? "")
            print(params)
            print(response.data ?? "")
            print(response.value ?? "")
            switch(response.result) {
            case .success(_):
                if let data = response.data{
                    do {
                        let gotData = try JSONDecoder().decode(T.self, from: data)
                        completion(gotData)

                    }
                    catch let jsonErr {
                        print("Error serializing json:", jsonErr)
                        ActivityIndicator.dismissActivityView()

                    }
                    DispatchQueue.main.async {
                        ActivityIndicator.dismissActivityView()
                    }
                }
                break
            case .failure(_):
                print(response.result.error ?? "")
                ActivityIndicator.dismissActivityView()


                break

            }
        }
}

// appel amusant

override func viewDidLoad() {
    super.viewDidLoad()

            let userID = ""
            let languageID = ""
            let params = ["userID":userID,"languageID":languageID]
            var appDelegate: AppDelegate?
            Service.fetchGenericData(urlString: "your url...", params: params) { (shortlistCountResponse : ShortlistCountResponse) in
             print(shortListCountResponse.data.totalprojFavcount ?? 0)

            }
}
0
kiran pm