Im essayant de comprendre les gestionnaires et les blocs d'achèvement. Je crois que vous pouvez utiliser des blocs pour de nombreuses choses de programmation profonde sans gestionnaires d'achèvement, mais je pense que je comprends que les gestionnaires d'achèvement sont basés sur des blocs. (Donc, fondamentalement, les gestionnaires d'achèvement ont besoin de blocs, mais pas l'inverse).
J'ai donc vu ce code sur Internet à propos de l'ancien framework Twitter:
[twitterFeed performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if (!error) {
self.successLabel.text = @"Tweeted Successfully";
[self playTweetSound];
} else {
// Show alert
}
// Stop indicator
sharedApplication.networkActivityIndicatorVisible = NO;
}];
Ici, nous appelons une méthode qui fait des trucs (effectue TWRequest) et retourne une fois terminé avec responseData & urlResponse & error. Ce n'est qu'à son retour qu'il exécute le bloc qui teste l'accord et arrête l'indicateur d'activité. PARFAIT!
Maintenant, c'est une configuration que j'ai pour une autre application qui fonctionne, mais j'essaie de rassembler les morceaux:
@interface
Define an ivar
typedef void (^Handler)(NSArray *users);
Declare the method
+(void)fetchUsersWithCompletionHandler:(Handler)handler;
@implementation
+(void)fetchUsersWithCompletionHandler:(Handler)handler {
//...Code to create NSURLRequest omitted...
__block NSArray *usersArray = [[NSArray alloc] init];
//A. Executes the request
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Peform the request
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
// Deal with your error
if (error) {
}
NSLog(@"Error %@", error);
return;
}
// Else deal with data
NSString *responseString = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
usersArray = [NSJSONSerialization JSONObjectWithData:[responseString dataUsingEncoding:NSASCIIStringEncoding] options:0 error:nil];
// Checks for handler & returns usersArray to main thread - but where does handler come from & how does it know to wait tip usersArray is populated?
if (handler){
dispatch_sync(dispatch_get_main_queue(), ^{
handler(usersArray);
});
}
});
}
Voici ma compréhension:
Mais fondamentalement, la demande est exécutée et l'erreur est traitée, les données sont traitées et ensuite, le gestionnaire est vérifié. Ma question est, comment fonctionne cette partie gestionnaire? Je comprends que si elle existe, elle sera renvoyée dans la file d'attente principale et retournera le tableau usersArray. Mais comment sait-il d'attendre que le tableau usersArray soit rempli? Je suppose que ce qui me déroute, c'est que la méthode: block dans ce cas a un autre bloc à l'intérieur, l'appel dispatch_async. Je suppose que ce que je cherche, c'est la logique qui fait vraiment des trucs et sait QUAND renvoyer la réponseData et urlResponse. Je sais que ce n'est pas la même application, mais je ne peux pas voir le code de performRequestWithHandler.
Fondamentalement, dans ce cas, cela fonctionne comme ça:
Puisqu'il s'agit de dispath_async, le thread actuel quitte la méthode fetchUsersWithCompletionHandler :.
...
le temps passe, jusqu'à ce que la file d'attente en arrière-plan dispose de ressources gratuites
...
Et maintenant, lorsque la file d'attente en arrière-plan est libre, elle consomme une opération planifiée (Remarque: elle exécute la requête synchrone - elle attend donc les données):
NSURLResponse *response;
NSError *error = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
...
Une fois que les données arrivent, alors sersArray est rempli.
Le code continue dans cette partie:
if (handler){
dispatch_sync(dispatch_get_main_queue(), ^{
handler(usersArray);
});
}
Maintenant, si nous avons spécifié un gestionnaire, il planifie le blocage de l'appel sur une file d'attente principale. Il s'agit de dispatch_sync, donc l'exécution sur le thread actuel ne se poursuivra que lorsque le thread principal aura terminé avec le bloc. À ce stade, le thread d'arrière-plan attend patiemment.
...
un autre moment passe
...
Maintenant, la file d'attente principale a des ressources libres, donc elle consomme au-dessus du bloc et exécute ce code (en passant le tableau des utilisateurs précédemment rempli au `` gestionnaire ''):
handler(usersArray);
Une fois cela fait, il revient du bloc et continue de consommer tout ce qu'il se trouve dans la file d'attente principale.
Edit: Quant aux questions que vous avez posées:
Ce n'est pas comme si la file d'attente principale/d'arrière-plan était toujours occupée, c'est peut-être juste. (en supposant que la file d'attente en arrière-plan ne prend pas en charge les opérations simultanées comme la principale). Imaginez le code suivant, qui est exécuté sur un thread principal:
dispatch_async(dispatch_get_main_queue(), ^{
//here task #1 that takes 10 seconds to run
NSLog(@"Task #1 finished");
});
NSLog(@"Task #1 scheduled");
dispatch_async(dispatch_get_main_queue(), ^{
//here task #2 that takes 5s to run
NSLog(@"Task #2 finished");
});
NSLog(@"Task #2 scheduled");
Puisque les deux sont dispatch_async
appels, vous planifiez leur exécution les uns après les autres. Mais la tâche n ° 2 ne sera pas traitée immédiatement par la file d'attente principale, car elle doit d'abord quitter la boucle d'exécution en cours, deuxièmement, elle doit d'abord terminer la tâche n ° 1.
Donc, la sortie du journal sera comme ça:
Task #1 scheduled
Task #2 scheduled
Task #1 finished
Task #2 finished
Vous avez:
typedef void (^Handler)(NSArray *users);
Qui déclare le type de bloc défini comme Handler
qui a void
type de retour et qui accepte NSArray *
comme paramètre.
Plus tard, vous avez votre fonction:
+(void)fetchUsersWithCompletionHandler:(Handler)handler
Qui prend comme bloc de paramètres de type Handler
et permet d'y accéder en utilisant le nom local handler
.
Et étape # 8:
handler(usersArray);
Qui appelle juste directement le bloc handler
(comme vous appeliez n'importe quelle fonction C/C++) et lui passe usersArray
comme paramètre.