完成处理程序如何在iOS上运行? [英] How does a completion handler work on iOS?

查看:234
本文介绍了完成处理程序如何在iOS上运行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图了解完成处理程序&块。我相信你可以在没有完成处理程序的情况下使用块进行许多深度编程,但我认为我理解完成处理程序是基于块的。 (所以基本上完​​成处理程序需要块,但不是相反)。

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 understand that completion handlers are based on blocks. (So basically completion handlers need blocks but not the other way around).

所以我在互联网上看到了关于旧Twitter框架的这段代码:

So I saw this code on the internet about the old twitter framework:

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

这里我们调用一个做东西的方法(执行TWRequest)并在完成responseData& urlResponse&错误。只有当它返回时才会执行测试授予的块并停止活动指示器。完美!

Here we are calling a method which does stuff (performs TWRequest) and returns when finished with responseData & urlResponse & error. Only when it returns does it execute the block which tests granted and stops the activity indicator. PERFECT!

现在这是我为不同的应用程序设置的设置,但我正在尝试将各个部分放在一起:

Now this is a setup I have for a different app which works, but I'm trying to put the pieces together:

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

以下是我的理解:


  1. fetchUsersWithCompletionHandler显然是performRequestWithHandler的同系物

  2. 不幸的是,这有点复杂,因为在路上有一个GCD调用...

但基本上,执行请求并处理错误,处理数据然后检查处理程序。我的问题是,这个处理程序部分如何工作?我知道如果它存在,那么它将发送回主队列并返回usersArray。但是,如果等到usersArray被填充,它怎么知道呢?我想让我感到困惑的是,在这种情况下,方法:block在其中有另一个块,即dispatch_async调用。我想我正在寻找的是实际做事的逻辑,并知道WHEN返回responseData和urlResponse。我知道它不是同一个应用程序,但我看不到performRequestWithHandler的代码。

But basically, the request is executed and the error is dealt with, the data is dealt with and then, the handler is checked. My question is, how does this handler part work? I understand if it exists then it will send back to the main queue and return the usersArray. But how does it know to wait until usersArray is populated? I guess whats confusing me is that fact that the method:block in this case has another block inside of it, the dispatch_async call. I guess what Im looking for is the logic that actually does stuff and knows WHEN to return the responseData and urlResponse. I know its not the same app, but I cant see the code for performRequestWithHandler.

推荐答案

基本上在这种情况下它的工作方式就是这样:

Basically in this case it works like that:


  1. 你可以从你喜欢的任何线程调用fetchUsersWithCompletionHandler :(可能是主线程)。

  2. 它初始化NSURLRequest,然后调用:
    dispatch_async(dispatch_get_global_queue ...
    基本上创建块,并安排它在后台队列上进行处理。

  3. 由于它是dispath_async,当前线程离开fetchUsersWithCompletionHandler:方法。

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

现在,当后台队列空闲时,它会消耗预定的操作(注意:它执行同步请求 - 所以它等待数据):

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];
...


  • 数据出现后, usersArray 已填充。

    代码继续到此部分:

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


  • 现在,如果我们指定了处理程序,它会调度块以进行调用在主队列上。它是dispatch_sync,因此在主线程完成块之前,当前线程上的执行将不会继续。此时,后台线程耐心地等待。

  • 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
    ...

    现在主队列有一些免费资源,所以它消耗了上面的块,并执行这段代码(将先前填充的usersArray传递给'handler'):

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


  • 一旦完成,它将从块返回并继续消耗它中的任何内容主队列。

  • Once it is done, it returns from the block and continues consuming whatever it is in the main queue.






    编辑:
    As对于你提出的问题:


    As for the questions you asked:


    1. 这不像主/后台队列总是很忙,它可能就是这样。 (假设后台队列不支持主要的并发操作)。想象一下以下代码,即在主线程上执行:

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


  • 因为两者都是 dispatch_async 调用,您可以将这些调度一个接一个地安排执行。但是主要队列不会立即处理任务#2,因为首先它必须离开当前执行循环,其次,它必须首先完成任务#1。

    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.你有:

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

    其中声明块类型为处理程序具有 void 返回类型,并接受 NSArray * 作为参数。

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

    稍后,你有你的功能:

    +(void)fetchUsersWithCompletionHandler:(Handler)handler
    

    作为 Handler类型的参数块并允许使用本地名称处理程序进行访问。

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

    步骤8:

    handler(usersArray);
    

    其中只是直接调用处理程序块(如你正在调用任何C / C ++函数)并将 usersArray 作为参数传递给它。

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

    这篇关于完成处理程序如何在iOS上运行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆