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?
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:
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
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()
}
}
}
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:
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:
* @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
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)
}
}