web-dev-qa-db-fra.com

Équivalent iOS / Objective-C d'AsyncTask d'Android

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?

68
SundayMonday

Réponse à la question d'origine:

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
});

Autres points:

1) Rappels

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
    });
});

2) Files d'attente globales

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.

3) Mémoire

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.

4) Tâches/blocs multiples

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.

101
eric.mitchell

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.

18
IgniteCoders

si vous ciblez une version iOS antérieure (que iOS 4 pour Grand Central Dispatch), vous pouvez utiliser les méthodes performSelector de NSObject

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");
}
5
JeanLuc

Swift 3

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
    }
}

Remarques

1
Suragch