完成处理程序在iOS上如何工作?
我试图了解完成处理程序和块。 我相信你可以使用块进行许多深层次的编程,而不需要完成处理程序,但是我想我明白,完成处理程序是基于块的。 (所以基本上完成处理程序需要块,而不是相反方向)。
所以我在互联网上看到了关于旧的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; }];
这里我们调用了一个方法,它执行TWRequest,并在返回responseData&urlResponse&error时返回。 只有当它返回时,才会执行被testing的块并停止活动指示器。 完善!
现在,这是一个设置,我有一个不同的应用程序的工作,但我试图把碎片放在一起:
@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); }); } }); }
这是我的理解:
- fetchUsersWithCompletionHandler显然是performRequestWithHandler的同系词
- 不幸的是这有点复杂,因为有一个GCD电话的方式…
但基本上,请求被执行并且错误被处理,数据被处理,然后处理器被检查。 我的问题是,这个处理程序部分是如何工作的? 我知道如果它存在,那么它会发回到主队列,并返回usersArray。 但是,它是如何知道要等到usersArray被填充的呢? 我想什么让我感到困惑的是,在这种情况下,方法:块中有另一个块,dispatch_async调用。 我想我在找什么是真正的东西的逻辑,知道什么时候返回responseData和urlResponse。 我知道它不是一样的应用程序,但我不能看到performRequestWithHandler的代码。
基本上在这种情况下它的工作原理是这样的:
- 你可以调用fetchUsersWithCompletionHandler:从任何你喜欢的线程(可能是主要的)。
- 它初始化NSURLRequest,然后调用:dispatch_async(dispatch_get_global_queue …基本上创build块,并调度它在后台队列处理。
-
由于是dispath_async,所以当前线程离开fetchUsersWithCompletionHandler:方法。
…
时间过去,直到后台队列有一些免费的资源
… -
而现在,当后台队列空闲时,它会消耗定时操作(注意:它执行同步请求 – 所以它等待数据):
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,所以当前线程的执行将不会继续,直到主线程完成。 此时,后台线程耐心等待。
…
另一时刻过去了
… -
现在主队列有一些免费的资源,所以它消耗上面的块,并执行这个代码(将先前填充的usersArray传递给'handler'):
handler(usersArray);
-
一旦完成,它将从块中返回并继续消耗主队列中的所有内容。
- 由于主线程是用块完成的,所以后台线程(卡在dispatch_sync中)可以继续进行。 在这种情况下,它只是从块返回。
编辑:至于你问的问题:
-
这不是像主/背景队列将总是忙,它只是它可能。 (假设后台队列不支持主要的并发操作)。 想象下面的代码,它正在主线程上执行:
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。
所以日志输出将如下所示:
Task #1 scheduled Task #2 scheduled Task #1 finished Task #2 finished
你有:
typedef void (^Handler)(NSArray *users);
其中声明块typededef作为Handler
程序具有void
返回types,并接受NSArray *
作为参数。
后来,你有你的function:
+(void)fetchUsersWithCompletionHandler:(Handler)handler
这需要作为Handler
types的参数块,并允许使用本地名称handler
访问它。
步骤#8:
handler(usersArray);
它只是直接调用handler
块(就像您正在调用任何C / C ++函数)并将usersArray
作为parameter passing给它。