1.5 用GCD执行与UI相关的任务
目的:为了并发你使用了GCD并且想知道与UI相关的APIs一起工作的最佳办法。
讨论:UI相关的任务必须在主线程中执行,所以主队列是在GCD中执行UI任务的唯一候选对象。我们可以使用dispatch_get_main_queue 函数得到处理分派队列的句柄。
这里有2中向主队列分派任务的方法,两者都是异步的,即使在任务没有执行的时候也让你的程序继续:
dispatch_async 函数
在分派队列上执行一个 Block Object.
dispatch_async_f函数
在分派队列上执行一个C函数。
dispatch_sync方法不能在主队列中调用,因为这会无限期的阻止线程并会导致你的应用死锁。所有通过GCD提交到主队列的任务必须是异步的。
dispatch_queue_t mainQueue = dispatch_get_main_queue();主队列用异步调用方法
dispatch_async 函数或 dispatch_async_f 函数
1.6 用GCD同步执行非UI相关的任务
目的:想要执行那些不包含任何UI相关代码的同步任务
方案:使用dispatch_sync 函数。
讨论:当执行那些与UI无关的任务,或者与UI交互任务时,和执行其他任务一样,会需要大量的时间,以上情况会经常出现。例如,你想下载一个图片并想在下载完成之后展现给用户。下载过程却和UI没有任何关系。
对于任何与UI无关的任务,你可以使用GCD中的全局并发队列。它们允许同步和异步执行。但是同步执行并不是说你的程序在继续之前要等到代码编程结束。这里是说并发队列会一直等待到你的任务完成后,才会再任务继续队列中的下一个代码块。
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);全局并发队列可以用同步和异步执行。
1.7 在GCD上异步执行非UI相关任务
目的:你想要在GCD的帮助下能够异步执行非UI相关任务。
方案:主队列、串行队列和并发队列上异步执行代码块才能见识到GCD的真正实力。(本书作者说,GCD会是多线程应用的未来,并将完全取代现代应用中的线程),千万注意:不要再主队列中使用同步执行函数dispatch_sync
要在分派队列上执行异步任务,你必须使用下面的这些函数中的其中一个:
dispatch_async 函数或者
dispatch_async_f 函数
我们来看个实例。我们来写一个iOS APP, 它能从网络的URL上下载一个图片。下载完成之后,APP应该将图片展示给用户。这个是我们的计划,那么为了达到这个目的我们该如何使用目前为止掌握的关于GCD的技能:
1、我们在并发队列上异步的启动一个block object。
2、在这个block中我们使用dispatch_sync函数来启动另一个Block Object,以从URL上下载图片。我这样做的目的是想让并发队列中剩余的代码能够等到图片下载完成之后在继续执行。从一个异步代码块上同步下载一个URL可以保持这个队列运行同步函数,而不是主线程。当我们从主线程角度来看的话整个运作仍是异步的。我们关心的问题是在下载图片的过程中没有阻塞主线程。
3、图片下载完毕后,为了在UI上将图片展示给用户,我们会在主线程上同步执行一个Block Object(见6.5)。
我们计划的框架就是这么简单:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(concurrentQueue, ^{ __block UIImage *image = nil; dispatch_sync(concurrentQueue, ^{ /* Download the image here */ }); dispatch_sync(dispatch_get_main_queue(), ^{ /* Show the image to the user here on the main queue*/ }); });
在第一个下载图片的同步调用结束之后,第二个dispatch_sync调用会在队列中执行,这个调用负责展示图片;这就是我们确切需要的东西,因为在图片展示给用户之前我们必须等图片完全下载。所以在图片下载完成之后,我们执行第二个Block Object,但这一次是在主队列上。
我们现在来下载图片并将其展示给用户。我们将通过iPhone App的一个视图控制器的viewDidAppear:方法来实现:
-(void)viewDidAppear:(BOOL)animated{ //在GCD上异步执行非UI相关任务 dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(concurrentQueue, ^{ __block UIImage *image = nil; dispatch_sync(concurrentQueue, ^{ /*download the image here*/ NSString *urlString = @"http://cms.csdnimg.cn/article/201310/09/5254b7b6c74cb.jpg"; NSURL *url = [NSURL URLWithString:urlString]; NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; NSError *downloadError = nil; NSData *imageData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:&downloadError]; if (downloadError == nil && imageData != nil) { image = [UIImage imageWithData:imageData]; } else if(downloadError != nil){ NSLog(@"Error happened = %@",downloadError); } else{ NSLog(@"No data could get download from the URL"); } }); dispatch_sync(dispatch_get_main_queue(), ^{ /*show the image to the user here on the main queue*/ if(image != nil){ //create the image view here UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds]; [imageView setImage:image]; /* make sure the image is not scaled incorrectly */ [imageView setContentMode:UIViewContentModeScaleAspectFit]; /* add the image to this view controller's view */ [self.view addSubview:imageView]; } else{ NSLog(@"Image isn't downloaded.NOthing to display"); } }); }); }
运行结果如下: