103 votes

Gestionnaire d'@scaping et de complétion Swift

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

Pero @escaping y Completion Handler sont trop difficiles à comprendre

J'ai cherché dans de nombreux affichages Swift et documents officiels, mais j'ai estimé que ce n'était toujours pas suffisant.

Voici 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 avait deux façons et raisons d'utiliser @escaping

La première sert à stocker une fermeture, la seconde à des fins de fonctionnement asynchrone.

Voici mes questions :

Premièrement, si doSomething s'exécute alors someFunctionWithEscapingClosure sera exécuté avec le paramètre de fermeture et cette fermeture sera sauvegardée dans le tableau des 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 se connecter à instance cet objet de SomeClass ?

Deuxièmement, je comprends someFunctionWithEscapingClosure comme ça.

Pour stocker la fermeture de la variable locale completionHandler à la variable globale "completionHandlers we using Mot-clé @escaping` !

sans @escaping mot-clé someFunctionWithEscapingClosure retours, variable locale completionHandler supprimera de la mémoire

@escaping c'est de garder cette fermeture dans la mémoire

C'est bien ça ?

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 autre fonction spécifique. Pourquoi ne pas simplement appeler 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 ?

127voto

Shobhakar Tiwari Points 4369

Manipulateur d'achèvement Swift s'échappant et ne s'échappant pas :

Comme l'explique Bob Lee dans son billet de blog Les gestionnaires de complétion en Swift avec Bob :

Supposons que l'utilisateur mette à jour une application pendant qu'il l'utilise. Vous voulez absolument avertir l'utilisateur lorsque la mise à jour est terminée. Vous voulez peut-être faire apparaître une boîte qui dit, "Félicitations, maintenant, vous pouvez profiter pleinement !"

Alors, comment exécuter un bloc de code seulement après que le téléchargement ait été terminé ? De plus, comment animer certains objets uniquement après le passage d'un contrôleur de vue à un autre ? contrôleur de vue a été déplacé vers le suivant ? Eh bien, nous allons découvrir comment en concevoir un comme un patron.

D'après ma liste de vocabulaire étendue, les gestionnaires d'achèvement signifient

Faire les choses quand elles ont été faites

L'article de Bob apporte des éclaircissements sur les gestionnaires d'achèvement (d'un point de vue de développeur, il définit exactement ce que nous devons comprendre).

@scaping closures :

Lorsqu'on passe une fermeture dans les arguments d'une fonction, son utilisation après le corps de la fonction est exécutée et renvoie le compilateur. Lorsque la fonction se termine, la portée de la fermeture passée existe et a une existence en mémoire, jusqu'à ce que la fermeture soit exécutée.

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

  • Stockage : Lorsque vous avez besoin de stocker la fermeture dans la variable globale, la propriété ou tout autre stockage qui existe dans la mémoire passée de la fonction d'appel obtenir exécuté et retourner le compilateur de retour.

  • Exécution asynchrone : Lorsque vous exécutez la fermeture de manière asynchrone sur une file d'attente, la file d'attente conservera la fermeture en mémoire pour vous, et pourra être utilisée ultérieurement. Dans ce cas, vous n'avez aucune idée du moment où 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 le site suivant cet article sur Medium .

J'ajoute un point supplémentaire, que tout développeur ios doit comprendre :

  1. Echapper à la fermeture : Une fermeture échappatoire est une fermeture qui est appelée après le retour de la fonction à laquelle elle a été passée. En d'autres termes, elle survit à la fonction à laquelle elle a été passée.
  2. Fermeture non évasée : Une fermeture qui est appelée à l'intérieur de la fonction dans laquelle elle a été passée, c'est-à-dire avant son retour.

0 votes

@shabhakar, que se passe-t-il si nous stockons une fermeture mais que nous ne l'appelons pas plus tard. Ou si la méthode est appelée deux fois mais que nous n'appelons la fermeture qu'une seule fois. Puisque nous savons que le résultat est le même.

0 votes

@user1101733 Je pense que vous parlez de l'échappement de la fermeture, la fermeture ne sera pas exécutée jusqu'à ce que vous ne l'appeliez pas. Dans l'exemple ci-dessus, si vous appelez la méthode doSomething 2 fois, 2 objets completionHandler seront ajoutés dans le tableau completionHandlers. Si vous prenez le premier objet du tableau completionHandlers et l'appelez, il s'exécutera mais le nombre d'objets completionHandlers restera le même (2).

0 votes

@Deepak, Oui à propos de la fermeture d'échappement. Supposons que nous n'utilisions pas de tableau et que nous utilisions une variable normale pour stocker la référence de la fermeture puisque nous voulons exécuter l'appel le plus récent. La mémoire sera-t-elle occupée par des fermetures précédentes qui ne seront jamais appelées ?

31voto

JamesK Points 191

Voici une petite classe d'exemples que j'utilise pour me rappeler comment fonctionne @escaping.

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 precedes/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 `runAsyncTask()` and tells us we need to allow our own completion
        //closure to be @escaping too. `runAsyncTask`'s completion block will be retained in memory until
        //it is executed, so our completion closure must explicitly do the same.
        runAsyncTask {
            completion()
        }
    }

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

}

2 votes

Ce code n'est pas correct. Il manque le @escaping qualificatifs.

0 votes

C'est ce que j'ai le plus aimé. i.e. escape the scope of this function.

3voto

yoAlex5 Points 2350

Fonction

Vous définissez une fonction avec l'option func mot-clé. Les fonctions peuvent prendre plusieurs paramètres et retourner aucun, un ou plusieurs paramètres.

Fermeture

Les closures sont des blocs autonomes de fonctionnalités qui peuvent être transmis et utilisés dans votre code. Les closures 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 et sont des types de première classe dans swift :

  • assigner une fonction/fermeture à une variable locale
  • passer une fonction/fermeture comme argument
  • retourner une fonction/fermeture

Fermeture échappatoire et fermeture non échappatoire

  • A non-escaping closure @noescape est une fermeture qui est appelée à l'intérieur de la fonction dans laquelle elle a été passée, c'est-à-dire avant son retour.

    Un bon exemple de non-escaping closure est sort function de tableau, comme sorted(by: (Element, Element) -> Bool) . La fermeture prend deux paramètres et renvoie un Bool qui détermine le résultat de la fonction de tri. La fermeture est appelée pendant l'exécution d'un calcul de tri.

    * @noescape était un attribut dans swift 2 . C'est déprécié de swift 3 . The @noescape attribute is applied by default in Swift 3 . Because closures are by default non-écriture in Swift 3, escaping closures need to be marked as such. And the L'attribut @escaping` nous permet de le faire.

  • Un site escaping closure @escaping est une fermeture qui est appelée après le retour de la fonction à laquelle elle a été passée. En d'autres termes, elle survit à la fonction à laquelle elle a été transmise. Les cas d'utilisation courants sont les suivants :

    • Appels asynchrones ; mise en réseau.
    • Fonctions stockées sous forme de variables ; pensez aux actions et aux rappels fournis.
    • Ordonnancement des tâches sur une file d'attente.

    Un bon exemple d'un escaping closure est un completion handler . De nombreuses fonctions qui lancent une opération asynchrone prennent un argument de fermeture comme gestionnaire d'achèvement. La fonction revient après avoir lancé 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.

Plus d'informations ici - Poste moyen , Poste moyen , docs

1voto

Hussein Points 31
/*the long story short is that @escaping means that don't terminate the function life time until the @escaping closure has finished execution in the opposite of nonEscaping closure the function can be terminated before the closure finishes execution Ex:
*/

 func fillData(completion: @escaping: () -> Void){ 
     /// toDo 
    completion()
  }

//___________________________

//The call for this function can be in either way's @escaping or nonEscaping :

fillData{
 /// toDo
}

/* again the deference between the two is that the function can be terminated before finish of execution nonEscaping closure in the other hand the @escaping closure guarantees that the function execution will not be terminated before the end of @escaping closure execution. Hope that helps ***#(NOTE THAT THE CLOSURE CAN BE OF ANY SWIFT DATA TYPE EVEN IT CAN BE TYPEALIAS)*/

0 votes

"Type de paramètre attendu après ':'"

-5voto

kiran pm Points 111
import UIKit
import Alamofire

// Modèle

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

}
class ShortlistcountData : Decodable {

    var totalpropFavcount : Int?
    var totalprojFavcount : Int?

}

// Classe générique Définition......

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 fun

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)

            }
}

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