2013-08-17 9 views
18

Ich versuche zu verstehen, Komplettierungshandler & Blöcke. Ich glaube, Sie können Blöcke für viele tiefgreifende Programmieraufgaben ohne Completion-Handler verwenden, aber ich denke, ich verstehe, dass Completion-Handler auf Blöcken basieren. (Im Grunde benötigen die Completion Handler also Blöcke, aber nicht umgekehrt).Wie funktioniert ein Completion-Handler auf iOS?

So habe ich diesen Code im Internet über den alten twitter Rahmen:

[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; 
    }]; 

Hier haben wir eine Methode aufrufen, die Sachen tut (führt TWRequest) und kehrt zurück, wenn mit response & urlResponse & Fehler beendet. Nur wenn es zurückkehrt, führt es den Block aus, der die gewährten Tests zulässt, und stoppt den Aktivitätsindikator. PERFEKT!

Nun ist dies ein Setup, das ich für eine andere App haben, die funktioniert, aber ich versuche, die Stücke zusammen zu stellen:

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

Hier ist mein Verständnis:

  1. fetchUsersWithCompletionHandler ist offensichtlich das Homolog von performRequestWithHandler
  2. ist dies leider ein wenig komplexer, weil es einen GCD Anruf im Weg ist ...

Aber im Grunde wird die Anfrage ausgeführt und der Fehler behandelt, die Daten behandelt und dann der Handler überprüft. Meine Frage ist, wie funktioniert dieser Handler Teil? Ich verstehe, wenn es existiert, dann wird es zurück zur Hauptwarteschlange senden und das usersArray zurückgeben. Aber wie kann es warten, bis usersArray aufgefüllt ist? Ich denke, was mich verwirrt, ist die Tatsache, dass die Methode: Block in diesem Fall einen anderen Block in sich hat, den Dispatch_Async-Aufruf. Ich denke, was ich suche, ist die Logik, die wirklich Sachen macht und weiß, WANN die AntwortData und URLResponse zurückgeben. Ich weiß, dass es nicht die gleiche App ist, aber ich kann den Code für performRequestWithHandler nicht sehen.

Antwort

29

Grundsätzlich in diesem Fall funktioniert es wie folgt aus:

  1. Sie fetchUsersWithCompletionHandler nennen: von welchem ​​Thread Sie mögen (wahrscheinlich wichtigste bilden).
  2. Er initialisiert NSURLRequest, ruft dann:. dispatch_async (dispatch_get_global_queue ... die im Grunde Block erzeugt, und Zeitpläne für die Verarbeitung auf einem Hintergrund Warteschlange
  3. Da es dispath_async ist, verlässt den aktuellen Thread fetchUsersWithCompletionHandler: Methode.

    ...
    Zeit vergeht, bis Hintergrund Warteschlange einige freie Ressourcen hat
    ...

  4. Und jetzt, wo die backgr ound Warteschlange ist frei, es verbraucht geplante Operation (Anmerkung: Es führt Synchron Anfrage - so wartet sie auf Daten):

    NSURLResponse *response; 
    NSError *error = nil; 
    NSData *receivedData = [NSURLConnection sendSynchronousRequest:request 
                  returningResponse:&response 
                     error:&error]; 
    ... 
    
  5. Sobald Daten kommt, dann ist die usersArray bevölkert ist.

  6. -Code weiterhin in diesem Teil:

    if (handler){ 
        dispatch_sync(dispatch_get_main_queue(), ^{ 
         handler(usersArray); 
        }); 
    } 
    
  7. Wenn wir nun Handler angegeben haben, ist es Zeitpläne für den Aufruf Block auf einer Hauptwarteschlange. Es ist dispatch_sync, daher wird die Ausführung im aktuellen Thread nicht fortgesetzt, bis der Hauptthread mit dem Block ausgeführt wird. An dieser Stelle wartet der Hintergrundfaden geduldig.

    ...
    ein anderer Moment geht
    ...

  8. Jetzt Haupt-Warteschlange einige freie Ressourcen hat, so dass es über Block verbraucht, und führt diesen Code (zuvor bevölkerten usersArray dem 'Handler' passing):

    handler(usersArray); 
    
  9. Sobald es fertig ist, ist es aus dem Block zurückkehrt und weiter raubend, was es in der Hauptwarteschlange ist.

  10. Da der Hauptthread mit dem Block ausgeführt wird, kann auch der Hintergrundthread (bei dispatch_sync stecken) weiter gehen. In diesem Fall kehrt es einfach vom Block zurück.

Edit: Was die Fragen Sie:

  1. Es ist nicht Haupt/Hintergrund-Warteschlange wie wird immer beschäftigt, es ist nur kann es sein. (Angenommen, die Hintergrundwarteschlange unterstützt keine gleichzeitigen Operationen wie die Hauptwarteschlange). Stellen Sie sich vor Sie folgenden Code ein, dass auf einem Haupt-Thread ausgeführt wird:

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

Da beide dispatch_async Anrufe sind, planen Sie diese zur Ausführung einer nach dem anderen. Task 2 wird jedoch nicht sofort von der Hauptwarteschlange verarbeitet, da sie zuerst die aktuelle Ausführungsschleife verlassen muss, und zweitens Aufgabe 1 zuerst beenden muss.

So ist die Log-Ausgabe wird so sein:

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

2.You haben:

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

die als Handler Block typedefe'd erklärt, dass void Rückgabetyp hat und dass akzeptiert NSArray * als Parameter.

Später haben Sie Ihre Funktion:

+(void)fetchUsersWithCompletionHandler:(Handler)handler 

, die als Parameterblock vom Typ es Handler und erlauben den Zugriff nimmt handler lokalen Namen.

Und Schritt # 8:

handler(usersArray); 

die gerade handler Block direkt aufruft (wie jedes C Aufruf wurden/C++ Funktion) und übergibt usersArray als Parameter zu.

+0

2 Fragen: (1) Warum sagen Sie in Schritt 3 "Zeit vergeht, bis die Hintergrundwarteschlange einige freie Ressourcen hat". Warum hätte es keine freien Ressourcen? Gleiches in den Schritten 7/8 mit der Hauptwarteschlange. Warum sollte es keine freien Ressourcen haben und deshalb warten müssen? Und das Wichtigste, wie behandelt "handler" den userArray, um ihn zurückzusenden? Welcher Teil des Codes weist den Handler an, Daten an den Absender zurückzusenden? – marciokoko

+0

Siehe meine bearbeitete Antwort. – deekay

+0

Was muss "NSData" in "NSString" und dann zurück in "NSData" konvertiert werden? – Rishab

Verwandte Themen