我们在开发的过程中,可能碰到如下的情况:异步下载两张图片,然后合并成一张图片。此时就需要使用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:@"https://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:@"https://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 = @"图片合并完毕";
});
});
}
@end
2.
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、基于线程安全的多读单写)