How does a completion handler work on iOS?

前端 未结 1 1054
再見小時候
再見小時候 2020-12-24 03:21

Im trying to understand completion handlers & blocks. I believe you can use blocks for many deep programming things without completion handlers, but I think i understan

相关标签:
1条回答
  • 2020-12-24 03:36

    Basically in this case it works like that:

    1. You call fetchUsersWithCompletionHandler: from whatever thread you like (probably form main one).
    2. It initializes NSURLRequest, then calls: dispatch_async(dispatch_get_global_queue... which basically creates block, and schedules it for processing on a background queue.
    3. Since it is dispath_async, current thread leaves the fetchUsersWithCompletionHandler: method.

      ...
      time passes, till background queue has some free resources
      ...

    4. And now, when the background queue is free, it consumes scheduled operation (Note: It performs synchronous request - so it waits for data):

      NSURLResponse *response;
      NSError *error = nil;
      NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
                                                   returningResponse:&response
                                                               error:&error];
      ...
      
    5. Once data comes, then the usersArray is populated.

    6. Code continues to this part:

      if (handler){
          dispatch_sync(dispatch_get_main_queue(), ^{
              handler(usersArray);
          });
      }
      
    7. Now, if we have handler specified, it schedules block for invocation on a main queue. It is dispatch_sync, so execution on current thread won't proceed till main thread will be done with the block. At this point, background thread patiently waits.

      ...
      another moment passes
      ...

    8. Now main queue has some free resources, so it consumes above block, and executes this code (passing previously populated usersArray to the 'handler'):

      handler(usersArray);
      
    9. Once it is done, it returns from the block and continues consuming whatever it is in the main queue.

    10. Since main thread is done with the block, also background thread (stuck at dispatch_sync) can proceed further. In this case it simply returns from block.

    Edit: As for the questions you asked:

    1. It's not like main/background queue will be always busy, it's just it may be. (assuming background queue does not support concurrent operations like the main one). Imagine following code, that is being executed on a main thread:

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

    Since both are dispatch_async calls, you schedule these for execution one after another. But task #2 won't be processed by main queue immediately, since first it has to leave current execution loop, secondly, it has to first finish task #1.

    So the log output will be like that:

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

    2.You have:

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

    Which declares block typedefe'd as Handler that has void return type and that accepts NSArray * as parameter.

    Later, you have your function:

    +(void)fetchUsersWithCompletionHandler:(Handler)handler
    

    Which takes as a parameter block of type Handler and allow access to it using local name handler.

    And step #8:

    handler(usersArray);
    

    Which just directly calls handler block (like you were calling any C/C++ function) and passes usersArray as a parameter to it.

    0 讨论(0)
提交回复
热议问题