我们在开发的过程中,可能碰到如下的情况:异步下载两张图片,然后合并成一张图片。此时就需要使用dispatch group,例子1如下面代码所示:
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create("com.jwl.www", DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, queue, ^{ NSLog(@"jwl 1111"); }); dispatch_group_async(group, queue, ^{ NSLog(@"jwl 2222"); }); dispatch_group_async(group, queue, ^{ NSLog(@"jwl 3333"); }); dispatch_group_notify(group, queue, ^{ NSLog(@"jwl finish"); });控制台输出结果:
2016-01-27 22:37:14.050 dispatch_group[1633:27456] jwl 2222 2016-01-27 22:37:14.050 dispatch_group[1633:27452] jwl 1111 2016-01-27 22:37:14.050 dispatch_group[1633:27466] jwl 3333 2016-01-27 22:37:14.050 dispatch_group[1633:27466] jwl finish前面三个线程并行执行,所以执行顺序不定。执行顺序会发生变化,但是jwl finish一定是最后输出!
// // ViewController.m // dispatch_group // // Created by bcc_cae on 16/1/27. // Copyright © 2016年 bcc_cae. All rights reserved. // #import "ViewController.h" @interface ViewController () @property (nonatomic,strong) UIImage *firstImage; @property (nonatomic,strong) UIImage *secondImage; @property (nonatomic,weak) UILabel *textLabel; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self composeImage]; } - (void)composeImage { UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectMake(200, 200, 0, 0)]; textLabel.text = @"正在下载图片"; [textLabel sizeToFit]; [self.view addSubview:textLabel]; self.textLabel = textLabel; [self group]; } - (void)group { UIImageView *imageView = [[UIImageView alloc] init]; [self.view addSubview:imageView]; dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create("cn.jwl.www", DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, queue, ^{ NSLog(@"正在下载第一张图片"); NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img.blog.csdn.net/20160114203205409?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"]]; NSLog(@"第一张图片下载完毕"); self.firstImage = [UIImage imageWithData:data]; }); dispatch_group_async(group, queue, ^{ NSLog(@"正在下载第二张图片"); NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://img.blog.csdn.net/20160114203205409?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"]]; NSLog(@"第二张图片下载完毕"); self.secondImage = [UIImage imageWithData:data]; }); dispatch_group_notify(group, queue, ^{ UIGraphicsBeginImageContext(CGSizeMake(300, 400)); [self.firstImage drawInRect:CGRectMake(0, 0, 150, 400)]; [self.secondImage drawInRect:CGRectMake(150, 0, 150, 400)]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); dispatch_async(dispatch_get_main_queue(), ^{ UIImageView *imageView = [[UIImageView alloc] initWithImage:newImage]; [self.view addSubview:imageView]; self.textLabel.text = @"图片合并完毕"; }); }); } @end2. dispatch_barrier_async
在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成后才能继续进行,使用 barrier 来等待之前任务完成,避免数据竞争等问题。 dispatch_barrier_async 函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后再执行 dispatch_barrier_async 函数追加的处理,等 dispatch_barrier_async 追加的处理执行结束之后,Concurrent Dispatch Queue才恢复之前的动作继续执行。
打个比方:比如你们公司周末跟团旅游,高速休息站上,司机说:大家都去上厕所,速战速决,上完厕所就上高速。超大的公共厕所,大家同时去,程序猿很快就结束了,但程序媛就可能会慢一些,即使你第一个回来,司机也不会出发,司机要等待所有人都回来后,才能出发。 dispatch_barrier_async 函数追加的内容就如同 “上完厕所就上高速”这个动作。
参考资料:
《招聘一个靠谱的iOS》面试题参考答案(下)
iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写)