Je connais bien l'utilisation de AsyncTask
dans Android: créez une sous-classe, appelez execute
sur une instance de la sous-classe et onPostExecute
est appelé sur le thread d'interface utilisateur ou le thread principal. Quel est l'équivalent dans iOS?
Grand Central Dispatch (GCD) offre un mécanisme pour effectuer des tâches en arrière-plan, bien qu'il fonctionne d'une manière structurellement différente d'AsyncTask. Pour effectuer quelque chose de manière asynchrone, il vous suffit de créer une file d'attente (comme un thread), puis de passer un bloc à dispatch_async()
à exécuter en arrière-plan. Je le trouve plus propre que AsyncTask, car il n'y a pas de sous-classement impliqué; c'est plus ou moins plug-and-play partout où vous avez du code que vous souhaitez exécuter en arrière-plan. Un exemple:
dispatch_queue_t queue = dispatch_queue_create("com.yourdomain.yourappname", NULL);
dispatch_async(queue, ^{
//code to be executed in the background
});
Si vous souhaitez effectuer une tâche en arrière-plan et mettre à jour l'interface utilisateur (ou faire quelque chose sur un autre thread) lorsque la tâche en arrière-plan est terminée, vous pouvez simplement imbriquer les appels de répartition:
dispatch_queue_t queue = dispatch_queue_create("com.yourdomain.yourappname", NULL);
dispatch_async(queue, ^{
//code to be executed in the background
dispatch_async(dispatch_get_main_queue(), ^{
//code to be executed on the main thread when background task is finished
});
});
Lors de la création d'une file d'attente, vous pouvez également utiliser la fonction dispatch_get_global_queue()
pour obtenir une file d'attente de répartition globale avec une certaine priorité (telle que DISPATCH_QUEUE_PRIORITY_HIGH
). Ces files d'attente sont universellement accessibles et sont utiles lorsque vous souhaitez affecter plusieurs tâches au même thread/file d'attente. Notez que la mémoire est entièrement gérée pour vous par iOS.
Il y a parfois une certaine confusion concernant la gestion de la mémoire et les files d'attente de répartition car elles ont leurs propres fonctions dispatch_retain
/dispatch_release
. Cependant, soyez assuré qu'ils sont traités comme des objets Objective-C par ARC, vous n'avez donc pas à vous soucier d'appeler ces fonctions. Référencement la grande réponse de rob mayoff concernant GCD et ARC, vous pouvez voir la documentation décrivant l'équivalence des files d'attente GCD avec les objets Objective-C:
* By default, libSystem objects such as GCD and XPC objects are declared as
* Objective-C types when building with an Objective-C compiler. This allows
* them to participate in ARC, in RR management by the Blocks runtime and in
* leaks checking by the static analyzer, and enables them to be added to Cocoa
* collections.
*
* NOTE: this requires explicit cancellation of dispatch sources and xpc
* connections whose handler blocks capture the source/connection object,
* resp. ensuring that such captures do not form retain cycles (e.g. by
* declaring the source as __weak).
*
* To opt-out of this default behavior, add -DOS_OBJECT_USE_OBJC=0 to your
* compiler flags.
*
* This mode requires a platform with the modern Objective-C runtime, the
* Objective-C GC compiler option to be disabled, and at least a Mac OS X 10.8
* or iOS 6.0 deployment target.
J'ajouterai que GCD a une interface de regroupement qui prend en charge la synchronisation de plusieurs blocs asynchrones si une tâche ne peut pas continuer tant que plusieurs activités asynchrones ne sont pas terminées. Jörn Eyrich et ɲeuroburɳ fournissent une explication généreuse de ce sujet ici . Si vous avez besoin de cette fonctionnalité, je vous recommande fortement de prendre quelques minutes pour lire attentivement leurs deux réponses et comprendre les différences entre elles.
Le documentation a une mine d'informations sur le sujet si vous êtes si enclin.
Il n'y a pas de classes pour cela dans iOS mais vous pouvez le simuler en utilisant des files d'attente. Tu peux appeler:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Your code to execute in background...
});
pour les tâches asynchrones et dans votre code asynchrone, appelez la prochaine file d'attente pour faire quelque chose dans la vue ...:
dispatch_async(dispatch_get_main_queue(), ^{
//Your code to execute on UIthread (main thread)
});
Ensuite, en utilisant ces deux files d'attente, vous pouvez créer une classe asyncTask, ajoutez cette classe à votre projet pour les implémenter:
//
// AsyncTask.h
//
// Created by Mansour Boutarbouch Mhaimeur on 25/10/13.
//
#import <Foundation/Foundation.h>
@interface AsyncTask : NSObject
- (void) executeParameters: (NSArray *) params;
- (void) preExecute;
- (NSInteger) doInBackground: (NSArray *) parameters;
- (void) postExecute: (NSInteger) result;
@end
//
// AsyncTask.m
//
// Created by Mansour Boutarbouch Mhaimeur on 25/10/13.
//
#import "AsyncTask.h"
@implementation AsyncTask
- (void) executeParameters: (NSArray *) params{
[self preExecute];
__block NSInteger result;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
result = [self doInBackground:params];
dispatch_async(dispatch_get_main_queue(), ^{
[self postExecute:result];
});
});
}
- (void) preExecute{
//Method to override
//Run on main thread (UIThread)
}
- (NSInteger) doInBackground: (NSArray *) parameters{
//Method to override
//Run on async thread (Background)
return 0;
}
- (void) postExecute: (NSInteger) result{
//Method to override
//Run on main thread (UIThread)
}
@end
Voici un exemple que j'utilise dans un projet:
#import "AsyncTask.h"
#import "Chat.h"
@interface SendChatTask : AsyncTask{
NSArray * chatsNotSent;
}
@end
#import "SendChatTask.h"
@implementation SendChatTask
- (void) preExecute{
//Method to override
}
- (NSInteger) doInBackground: (NSArray *) parameters{
//Method to override
NSString *sendChatsURL = [NSString stringWithFormat:@"%@%@%@",Host, NAMESPACE,URL_SEND_CHAT];
chatsNotSent = [parameters objectAtIndex:0];
NSString *response;
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
//...
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[ChatJSONParser wrapChatArray: chatsNotSent] options:0 error:&error];
NSString *JSONString = [[NSString alloc] initWithBytes:[jsonData bytes] length:[jsonData length] encoding:NSUTF8StringEncoding];
[params setObject:JSONString forKey:@"chats"];
response = [HTTPClient executePOST:sendChatsURL parameters:params];
if([respuesta isEqualToString:@"true"]){
return 1;
}else{
return -1;
}
}
- (void) postExecute: (NSInteger) result{
//Method to override
if (result == 1) {
for (Chat *chat in chatsNotSent) {
chat.state = STATE_NOT_SENT;
[chat save];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate refreshChat];
}
} else {
}
}
@end
Et l'appel suivant:
[[[SendChatTask alloc] init] executeParameters:[NSArray arrayWithObjects: chatsNotSent, nil]];
Vous pouvez ajouter une méthode de mise à jour publishProgress()
et respective ... Je ne l'utilise pas pour le moment car j'appelle ma tâche asynchrone dans les services d'arrière-plan.
J'espère que c'est utile.
si vous ciblez une version iOS antérieure (que iOS 4 pour Grand Central Dispatch), vous pouvez utiliser les méthodes performSelector de NSObject
Exécuter en arrière-plan Thread performSelectorInBackground: withObject:
Et exécuter sur MainThread performSelectorOnMainThread: withObject: waitUntilDone:
Ceci est un exemple:
[self performSelectorInBackground:@selector(executeInBackground) withObject:nil];
-(void) executeInBackground
{
NSLog(@"executeInBackground");
[self performSelectorOnMainThread:@selector(executeOnMainThread) withObject:nil waitUntilDone:NO];
}
-(void) executeOnMainThread
{
NSLog(@"executeOnMainThread");
}
Dans Android lorsque je voulais exécuter une tâche sur un thread d'arrière-plan, puis mettre à jour l'interface utilisateur une fois terminée, j'ai utilisé AsyncTask
( exemple ). Maintenant, lorsque je crée des versions iOS de mes applications, j'utilise Grand Central Dispatch (GCD) pour faire la même chose. Voici comment cela se fait avec Swift:
DispatchQueue.global(qos: .background).async {
// code to be run on a background task
DispatchQueue.main.async {
// code to be run on the main thread after the background task is finished
}
}