假设有一个音乐应用,如果要执行多个下载歌曲的任务,这些耗时的任务会被放到多个线程上异步执行,直到全部的歌曲下载完成,弹出一个提示框来通知用户歌曲已下载完成。
针对这个应用场景,可以考虑使用队列组。一个队列组可以将多个block组成一组,用于监听这一组任务是否全部完成,指导关联的任务全部完成后再发出通知以执行其他的操作。iOS提供了如下的函数开始用队列组。
(1)创建队列组
要想使用队列组,首先需要创建一个队列组对象,可以通过dispatch_group_create()函数来创建,它的定义格式如下:
dispatch_group_t dispatch_group_create(void);
在上述格式中,该函数无需传入任何参数,其返回值是
dispatch_group_t类型的。
(2)调用队列组
创建了dispatch_group_t对象后,可以使用dispatch_group_async()函数将block提交至一个队列,同时将这些block添加到一个组里面,函数格式如下:
void dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
在上述格式中,该函数没有返回值,它需要传入3个参数,第1个参数是创建的队列组,第2个参数是将要添加到的队列,第3个参数是将要执行的代码块。需要注意的是,该函数的名称有一个
async标志,表示这个组会异步地执行这些代码块。
(3)通知
当全部的任务执行完成后,通知执行其他的操作,通过dispatch_group_notify()函数来通知,它的定义格式如下:
void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
在上述定义格式中,该函数需要传入3个参数,第1个参数表示创建的队列组,第2个参数表示其他任务要添加到的队列,第3个参数表示要执行的其他代码块。
接下来通过模拟一个需求来展示调度队列组,就是从网上加载两张图片,进行组合后,最终显示到一个ImageView上。根据这个需求,通过代码完成相应的逻辑,具体步骤如下:
(1)新建一个SingleViewApplication工程,命名为08-Dispatch Group;
(2)进入Main.StoryBoard,从对象库中拖拽一个ImageView到程序界面,用于显示组合后的图片;
(3)通过拖拽的方式,将ImageView在viewController.m文件的类扩展中进行属性的声明;
(4)单击屏幕,依次从网络上加载两张图片,直到这两张图片下载完成,将这两张图片进行组合,最终回到主线程上显示,代码如下:
#import "ViewController.h"
//宏定义全局并发队列
#define global_queue dispatch_get_global_queue(0,0)
//宏定义主队列
#define main_queue dispatch_get_main_queue()
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self groupImage];
}
/**
*使用队列组组合图片
*/
dispatch_group_t dispatch_group_create(void);
void dispatch_group_notify(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
-(void)groupImage
{
//1、创建一个队列组和队列
dispatch_group_t group=dispatch_group_create();
//2、下载第1张图片
__block UIImage *image1=nil;//定义了__block修饰的一个属性,能在block中修改变量
dispatch_group_async(group, global_queue,^{
image1=[self downloadImage:@"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=4133347000,1310938944&fm=27&gp=0.jpg"];
});
//3、下载第2张图片
__block UIImage *image2=nil;//定义了__block修饰的另一个属性,能在block中修改变量
dispatch_group_async(group, global_queue,^{
image2=[self downloadImage:@"https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1322912466,2607748595&fm=27&gp=0.jpg"];
});
//4、合并图片
dispatch_group_notify(group, global_queue, ^{
//4、1开启一个位图上下文
UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
//4、2绘制第1张图片
CGFloat image1W=image1.size.width;
CGFloat image1H=image1.size.height;
[image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
//4、3绘制第2张图片
CGFloat image2W=image2.size.width*0.3;
CGFloat image2H=image2.size.height*0.3;
CGFloat image2Y=image1H-image2H;
[image2 drawInRect:CGRectMake(140, image2Y, image2W, image2H)];
//4、4得到上下文的图片
UIImage *fullImage=UIGraphicsGetImageFromCurrentImageContext();
//4、5结束上下文
UIGraphicsEndImageContext();
//4、6回到主线程显示图片
dispatch_async(main_queue,^{
self.imageView.image=fullImage;
});
});
}
//封装一个方法,只要传入一个URL参数,就返回一张网络上下载的图片
-(UIImage *)downloadImage:(NSString *)urlStr{
NSURL *imageUrl=[NSURL URLWithString:urlStr];
NSData *data=[NSData dataWithContentsOfURL:imageUrl];
return [UIImage imageWithData:data];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
运行程序,程序运行成功后,单击模拟器屏幕,可见第1张人物图片和第二张百度logo图片组合在一起,形成一张图片显示到屏幕上,如下图所示: