¿Cómo funciona un manejador de finalización en iOS?

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

Entonces vi este código en Internet sobre el viejo 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 shanetworkingApplication.networkActivityIndicatorVisible = NO; }]; 

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

Ahora esta es una configuration que tengo para una aplicación diferente que funciona, pero estoy intentando poner las piezas juntas:

 @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 path …

Pero, básicamente, la request se ejecuta y se trata el error, se tratan los datos y, a continuación, se verifica el manejador. Mi pregunta es: ¿cómo funciona esta parte del controller? Entiendo que si existe, enviará de nuevo a la queue principal y devolverá usersArray. ¿Pero cómo sabe esperar hasta que usersArray se rellene? Creo que lo que me confunde es el hecho de que el método: bloque en este caso tiene otro bloque dentro de él, la llamada dispatch_async. Creo que lo que estoy buscando es la lógica que realmente hace cosas y sabe CUÁNDO devolver el responseData y urlResponse. Sé que no es la misma aplicación, pero no puedo ver el código para performRequestWithHandler.

Básicamente en este caso funciona así:

  1. Llama a fetchUsersWithCompletionHandler: desde cualquier hilo que te guste (probablemente forme uno principal).
  2. Inicializa NSURLRequest, luego llama: dispatch_async (dispatch_get_global_queue … que básicamente crea un bloque y lo progtwig para procesar en una queue de background.
  3. Como es dispath_async, el hilo actual abandona el método fetchUsersWithCompletionHandler:.


    el time pasa, hasta que la queue de background tiene algunos resources gratuitos

  4. Y ahora, cuando la queue de background está libre, consume la operación progtwigda (Nota: realiza una request síncrona , por lo que espera datos):

     NSURLResponse *response; NSError *error = nil; NSData *receivedData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; ... 
  5. Una vez que llegan los datos, se rellena usersArray .

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

     if (handler){ dispatch_sync(dispatch_get_main_queue(), ^{ handler(usersArray); }); } 
  7. Ahora, si tenemos el controller especificado, progtwig el bloque para la invocación en una queue 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 background espera pacientemente.


    pasa otro momento

  8. Ahora la queue principal tiene algunos resources gratuitos, por lo que consume por encima del bloque, y ejecuta este código (pasando usersArray previamente poblado al 'manejador'):

     handler(usersArray); 
  9. Una vez hecho, vuelve del bloque y continúa consumiendo lo que sea en la queue principal.

  10. Como el hilo principal se realiza con el bloque, también el hilo de background (atascado en dispatch_sync) puede continuar. En este caso, simplemente vuelve del bloque.

Editar: en cuanto a las preguntas que hizo:

  1. No es como si la queue de background / principal estuviera siempre ocupada, es posible. (suponiendo que la queue de background no admite operaciones simultáneas 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 , se progtwign estas para ejecución una tras otra. Pero la tarea # 2 no será procesada por la queue principal inmediatamente, ya que primero tiene que abandonar el ciclo de ejecución actual, en segundo lugar, primero debe terminar la tarea # 1.

Entonces, la salida del logging será así:

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

2. Tienes:

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

Lo que declara bloquear typedefe'd como Handler que tiene el tipo de retorno void y que acepta NSArray * como parámetro.

Más tarde, tienes tu function:

 +(void)fetchUsersWithCompletionHandler:(Handler)handler 

Que toma como un bloque de parameters de tipo Handler y permite el acceso a él usando el handler local de nombres.

Y paso # 8:

 handler(usersArray); 

Lo que llama directamente al bloque de handler (como si estuviera llamando a cualquier function de C / C ++) y pasa a usersArray como un parámetro.