guidelines buttons bar ios objective-c-blocks completionhandler

buttons - status bar ios



¿Cómo funciona un controlador de finalización en iOS? (1)

Estoy tratando de entender los controladores y bloques de finalización. Creo que puedes usar bloques para muchas cosas de programación profunda sin controladores de finalización, pero creo que entiendo que los controladores de finalización se basan en bloques. (Así que, básicamente, los controladores de finalización necesitan bloques, pero no al revés).

Así que vi este código en internet sobre el antiguo marco de 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; }];

Aquí estamos llamando a un método que hace cosas (realiza TWRequest) y devuelve cuando termina con responseData & urlResponse & error. Solo cuando regresa ejecuta el bloque que realiza las pruebas y detiene el indicador de actividad. ¡PERFECTO!

Esta es una configuración que tengo para una aplicación diferente que funciona, pero estoy tratando de unir las piezas:

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

Aquí está mi entendimiento:

  1. fetchUsersWithCompletionHandler es obviamente el homólogo de performRequestWithHandler
  2. desafortunadamente esto es un poco más complejo porque hay una llamada GCD en el camino ...

Pero básicamente, la solicitud se ejecuta y el error se resuelve, los datos se tratan y, a continuación, se comprueba el controlador. Mi pregunta es, ¿cómo funciona esta parte de controlador? Entiendo que si existe, se enviará de vuelta a la cola principal y devolverá la matriz de usuarios. Pero, ¿cómo saber esperar hasta que se rellene usersArray? Supongo que lo que me confunde es el hecho de que el método: el bloque en este caso tiene otro bloque dentro de él, la llamada dispatch_async. Supongo que lo que estoy buscando es la lógica que realmente hace las cosas y sabe cuándo devolver los datos de respuesta y urlResponse. Sé que no es la misma aplicación, pero no puedo ver el código de performRequestWithHandler.


Básicamente en este caso funciona así:

  1. Llamas a fetchUsersWithCompletionHandler: desde cualquier hilo que te guste (probablemente sea el principal).
  2. Inicializa NSURLRequest, luego llama: dispatch_async (dispatch_get_global_queue ... que básicamente crea un bloque y lo programa para procesarlo en una cola de fondo).
  3. Dado que es dispath_async, el subproceso actual deja el método fetchUsersWithCompletionHandler :.

    ...
    el tiempo pasa, hasta que la cola de fondo tiene algunos recursos gratuitos
    ...

  4. Y ahora, cuando la cola en segundo plano está libre, consume la operación programada (Nota: realiza una solicitud síncrona , por lo que espera los datos):

    NSURLResponse *response; NSError *error = nil; NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; ...

  5. Una vez que los datos llegan, la matriz de usuarios se rellena.

  6. El código continúa a esta parte:

    if (handler){ dispatch_sync(dispatch_get_main_queue(), ^{ handler(usersArray); }); }

  7. Ahora, si tenemos un controlador especificado, programa el bloqueo para la invocación en una cola principal. Es dispatch_sync, por lo que la ejecución en el hilo actual no continuará hasta que el hilo principal se realice con el bloque. En este punto, el hilo de fondo espera pacientemente.

    ...
    pasa otro momento
    ...

  8. Ahora la cola principal tiene algunos recursos libres, por lo que consume el bloque anterior, y ejecuta este código (pasando los usuarios previamente rellenados a la "controladora"):

    handler(usersArray);

  9. Una vez hecho esto, regresa del bloque y continúa consumiendo lo que sea que esté en la cola principal.

  10. Dado que el subproceso principal se realiza con el bloque, también puede continuar el subproceso en segundo plano (atascado en dispatch_sync). En este caso simplemente vuelve del bloque.

Edit: En cuanto a las preguntas que hiciste:

  1. No es como si la cola principal / de fondo estuviera siempre ocupada, es posible. (Suponiendo que la cola de fondo no admita operaciones concurrentes como la principal). Imagine el siguiente código, que se está ejecutando en un hilo 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");

Dado que ambas son llamadas dispatch_async , puede programarlas para que se ejecuten una tras otra. Pero la tarea # 2 no será procesada inmediatamente por la cola principal, ya que primero debe abandonar el bucle de ejecución actual, y luego, debe terminar primero la tarea # 1.

Así que la salida del registro será así:

Task #1 scheduled Task #2 scheduled Task #1 finished Task #2 finished

2.Usted tiene:

typedef void (^Handler)(NSArray *users);

Que declara bloque typedefe''d como Handler que tiene un tipo de retorno void y que acepta NSArray * como parámetro.

Más tarde, tienes tu función:

+(void)fetchUsersWithCompletionHandler:(Handler)handler

Lo que toma como un bloque de parámetros de tipo Handler y permite el acceso a él usando un handler nombre local.

Y paso # 8:

handler(usersArray);

Que simplemente llama directamente al bloque del handler (como si estuviera llamando a cualquier función de C / C ++) y le pasa a usersArray como un parámetro.