iOS/Objective-C equivalente de AsyncTask de Android

6 minutos de lectura

avatar de usuario
Domingo Lunes

estoy familiarizado con el uso AsyncTask en Android: crea una subclase, llama execute en una instancia de la subclase y onPostExecute se llama en el subproceso de la interfaz de usuario o en el subproceso principal. ¿Cuál es el equivalente en iOS?

avatar de usuario
eric.mitchell

Respuesta a la pregunta original:

Grand Central Dispatch (GCD) ofrece un mecanismo para realizar tareas en segundo plano, aunque funciona de una manera estructuralmente diferente a AsyncTask. Para realizar algo de forma asíncrona, solo necesita crear una cola (como un hilo) y luego pasar un bloque a dispatch_async() que se realizará en segundo plano. Lo encuentro más ordenado que AsyncTask, ya que no hay subclases involucradas; es más o menos plug-and-play dondequiera que tenga código que le gustaría ejecutar en segundo plano. Un ejemplo:

dispatch_queue_t queue = dispatch_queue_create("com.yourdomain.yourappname", NULL);
dispatch_async(queue, ^{
    //code to be executed in the background
});

Otros puntos:

1) Devoluciones de llamada

Si desea realizar una tarea en segundo plano y actualizar la interfaz de usuario (o hacer algo en otro subproceso) cuando finalice la tarea en segundo plano, simplemente puede anidar las llamadas de despacho:

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) Colas globales

Al crear una cola, también puede utilizar el dispatch_get_global_queue() función para obtener una cola de envío global con una cierta prioridad (como DISPATCH_QUEUE_PRIORITY_HIGH). Estas colas son universalmente accesibles y son útiles cuando desea asignar varias tareas al mismo hilo/cola. Tenga en cuenta que iOS administra completamente la memoria por usted.

3) Memoria

A veces hay cierta confusión con respecto a la administración de la memoria y las colas de envío porque tienen sus propias dispatch_retain/dispatch_release funciones Sin embargo, tenga la seguridad de que ARC los trata como objetos Objective-C, por lo que no necesita preocuparse por llamar a estas funciones. Haciendo referencia a la gran respuesta de rob mayoff con respecto a GCD y ARC, puede ver la documentación que describe la equivalencia de las colas GCD con los objetos 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) Múltiples Tareas/Bloques

Agregaré que GCD tiene una interfaz de agrupación que admite la sincronización de múltiples bloques asíncronos si una tarea no puede continuar hasta que se hayan completado varias actividades asíncronas. Jörn Eyrich y ɲeuroburɳ brindan aquí una generosa explicación de este tema. Si necesita esta funcionalidad, le recomiendo encarecidamente que se tome unos minutos para leer ambas respuestas detenidamente y comprender las diferencias entre ellas.

los documentación tiene una gran cantidad de información sobre el tema si así lo desea.

  • Interesante. ¿Hay una devolución de llamada que se ejecuta en el hilo principal?

    – Domingo Lunes

    30 de julio de 2012 a las 19:26

  • No queue hay que destruirlo con dispatch_release?

    – Nick

    25 de diciembre de 2013 a las 17:13

  • A partir de iOS 6.0 (y OS X 10.8), ARC administrará los objetos Grand Central Dispatch como si fueran objetos Objective-C. Entonces, si su destino de implementación es < iOS 6.0 o OS X 10.8, entonces sí, queue debe ser liberado explícitamente. De lo contrario, no, ARC se encargará.

    – eric.mitchell

    13 de febrero de 2014 a las 20:01

avatar de usuario
IgniteCoders

No hay clases para eso en iOS, pero puedes simularlo usando colas. Puedes llamar:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //Your code to execute in background...
});

para tareas asíncronas y dentro de su código asíncrono llame a la siguiente cola para hacer algo en la vista…:

dispatch_async(dispatch_get_main_queue(), ^{
    //Your code to execute on UIthread (main thread)
});

Luego, usando estas dos colas, puede crear una clase asyncTask, agregue esta clase a su proyecto para implementarlas:


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

Este es un ejemplo que estoy usando en un proyecto:


#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

Y la siguiente llamada:

[[[SendChatTask alloc] init] executeParameters:[NSArray arrayWithObjects: chatsNotSent, nil]];

Puedes agregar un publishProgress() método de actualización y respectivo … No lo uso por el momento porque llamo a mi tarea asíncrona en servicios en segundo plano.

Espero que sea útil.

  • probablemente más descriptivo de lo necesario… lo malo de copiar/pegar

    – IgniteCoders

    2 oct 2014 a las 10:52

si su objetivo es una versión anterior de iOS (que iOS 4 para Grand Central Dispatch), puede usar los métodos NSObject performSelector

Esto es un ejemplo:

[self performSelectorInBackground:@selector(executeInBackground) withObject:nil];


-(void) executeInBackground
{
    NSLog(@"executeInBackground");

    [self performSelectorOnMainThread:@selector(executeOnMainThread) withObject:nil waitUntilDone:NO];
}

-(void) executeOnMainThread
{
    NSLog(@"executeOnMainThread");
}

  • Menos del 5% de los usuarios de iOS son <4.0

    – eric.mitchell

    30 de julio de 2012 a las 19:48

  • Estoy totalmente de acuerdo, que la optimización para versiones bajas de iOS probablemente no siempre valga la pena. Pero prefiero el ObjectiveC-API de performSelector que el API en C de Grand Central Dispatch. la parte de iOS 2+ es solo una característica adicional.

    – JeanLuc

    30/07/2012 a las 20:00

  • Sí, entiendo eso. De hecho, me gusta más la sintaxis de estilo C porque no necesita crear un nuevo método para cada tarea, y compartir variables es más fácil (los métodos NSObject solo pueden tomar un parámetro, y pasar una variedad de objetos es complicado)

    – eric.mitchell

    30/07/2012 a las 20:30

  • @JeanLuc ¿Cuáles son las implicaciones de rendimiento entre esta solución y la de @Rickay?

    – Domingo Lunes

    30 de julio de 2012 a las 21:41


  • Honestamente no puedo decir. Es posible que en tiempo de ejecución, ambos hagan más o menos lo mismo, pero la sintaxis es diferente. Realmente no lo sé. stackoverflow.com/questions/5653522/… podría brindarle información

    – eric.mitchell

    30 de julio de 2012 a las 21:46

avatar de usuario
Suragch

rápido 3

En Android, cuando quería ejecutar una tarea en un subproceso en segundo plano y luego actualizar la interfaz de usuario cuando terminaba, usaba AsyncTask (ejemplo). Ahora, cuando hago versiones iOS de mis aplicaciones, uso Gran despacho central (GCD) para hacer lo mismo. Así es como se hace con 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
    }
}

notas

Aquí está la versión ac# Xamarin.iOS con PusblishProgress:

internal abstract class AsyncTask : NSObject
{
    protected abstract nint DoInBackground(NSArray parameters);

    protected abstract void PostExecute(nint result);

    public void ExecuteParameters(NSArray @params)
    {
        this.PreExecute();

        DispatchQueue.GetGlobalQueue(DispatchQueuePriority.Default).DispatchAsync(() =>
        {
            //We're on a Background thread
            var result = this.DoInBackground(@params);
            DispatchQueue.MainQueue.DispatchAsync(() => {
                // We're on the main thread
                this.PostExecute(result);
            });
        });

    }

    protected abstract void PreExecute();

    protected void PublishProgress(NSArray parameters)
    {
        InvokeOnMainThread(() => {
            // We're on the main thread
            this.OnProgressUpdate(parameters);
        });
    }

    protected abstract void OnProgressUpdate(NSArray parameters);
}

E implementación:

internal class MyAsyncTask : AsyncTask
{
    protected override void OnProgressUpdate(NSArray parameters)
    {
        // This runs on the UI Thread
    }

    protected override nint DoInBackground(NSArray parameters)
    {
        // Do some background work
        // ....
        var progress = NSArray.FromObjects(1, "Done step 1");
        PublishProgress(progress);

        return 0;
     }

     protected override void PostExecute(nint result)
     {
         // This runs on the UI Thread

     }

     protected override void PreExecute()
     {
        // This runs on the UI Thread

     }
}

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad