-
iOS开发多线程篇—GCD的常见用法
一、延迟执行 1.介绍 iOS常见的延时执行有2种方式(1)调用NSObject的方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再调用self的run方法
(2)使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});
2.说明
第一种方法,该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程。[self performSelector:@selector(run) withObject:nil afterDelay:3.0];
说明:在3秒钟之后,执行run函数
代码示例:
01.1//02.2// YYViewController.m03.3// 01-GCD的常见使用(延迟执行)04.4//05.5// Created by apple on 14-6-25.06.6// Copyright (c) 2014年 itcase. All rights reserved.07.7//08.809.9#import"YYViewController.h"10.1011.11@interfaceYYViewController ()12.1213.13@end14.1415.15@implementationYYViewController16.1617.17- (void)viewDidLoad18.18{19.19[superviewDidLoad];20.20NSLog(@"打印线程----%@",[NSThread currentThread]);21.21//延迟执行22.22//第一种方法:延迟3秒钟调用run函数23.23[self performSelector:@selector(run) withObject:nil afterDelay:2.0];24.2425.25}26.26-(void)run27.27{28.28NSLog(@"延迟执行----%@",[NSThread currentThread]);29.29}30.3031.31-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event32.32{33.33//在异步函数中执行34.34dispatch_queue_t queue = dispatch_queue_create("wendingding",0);35.3536.36dispatch_sync(queue, ^{37.37[self performSelector:@selector(test) withObject:nil afterDelay:1.0];38.38});39.39NSLog(@"异步函数");40.40}41.41-(void)test42.42{43.43NSLog(@"异步函数中延迟执行----%@",[NSThread currentThread]);44.44}45.45@end说明:如果把该方法放在异步函数中执行,则方法不会被调用(BUG?)
第二种方法,
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//延迟执行的方法
});
说明:在5秒钟之后,执行block中的代码段。
参数说明:

什么时间,执行这个队列中的这个任务。
代码示例:
01.1//02.2// YYViewController.m03.3// 02-GCD常见使用(延迟执行2)04.4//05.5// Created by apple on 14-6-25.06.6// Copyright (c) 2014年 itcase. All rights reserved.07.7//08.809.9#import"YYViewController.h"10.1011.11@interfaceYYViewController ()12.1213.13@end14.1415.15@implementationYYViewController16.1617.17- (void)viewDidLoad18.18{19.19[superviewDidLoad];20.2021.21NSLog(@"打印当前线程---%@", [NSThread currentThread]);22.2223.23//延迟执行,第二种方式24.24//可以安排其线程(1),主队列25.25dispatch_queue_t queue= dispatch_get_main_queue();26.26dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0* NSEC_PER_SEC)), queue, ^{27.27NSLog(@"主队列--延迟执行------%@",[NSThread currentThread]);28.28});29.2930.30//可以安排其线程(2),并发队列31.31//1.获取全局并发队列32.32dispatch_queue_t queue1= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);33.33//2.计算任务执行的时间34.34dispatch_time_t when=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0* NSEC_PER_SEC));35.35//3.会在when这个时间点,执行queue中的这个任务36.36dispatch_after(when, queue1, ^{37.37NSLog(@"并发队列-延迟执行------%@",[NSThread currentThread]);38.38});39.39}40.4041.41@end
延迟执行:不需要再写方法,且它还传递了一个队列,我们可以指定并安排其线程。
如果队列是主队列,那么就在主线程执行,如果队列是并发队列,那么会新开启一个线程,在子线程中执行。
二、一次性代码
1.实现一次性代码
需求:点击控制器只有第一次点击的时候才打印。
实现代码:
01.1//02.2// YYViewController.m03.3// 03-GCD常见使用(一次性代码)04.4//05.5// Created by apple on 14-6-25.06.6// Copyright (c) 2014年 itcase. All rights reserved.07.7//08.809.9#import"YYViewController.h"10.1011.11@interfaceYYViewController ()12.12@property(nonatomic,assign) BOOL log;13.13@end14.1415.15@implementationYYViewController16.1617.17-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event18.18{19.19if(_log==NO) {20.20NSLog(@"该行代码只执行一次");21.21_log=YES;22.22}23.23}24.24@end缺点:这是一个对象方法,如果又创建一个新的控制器,那么打印代码又会执行,因为每个新创建的控制器都有自己的布尔类型,且新创建的默认为NO,因此不能保证改行代码在整个程序中只打印一次。
2.使用dispatch_once一次性代码
使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
整个程序运行过程中,只会执行一次。
代码示例:
01.1//02.2// YYViewController.m03.3// 03-GCD常见使用(一次性代码)04.4//05.5// Created by apple on 14-6-25.06.6// Copyright (c) 2014年 itcase. All rights reserved.07.7//08.809.9#import"YYViewController.h"10.1011.11@interfaceYYViewController ()12.12@property(nonatomic,assign) BOOL log;13.13@end14.1415.15@implementationYYViewController16.1617.17//-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event18.18//{19.19// if (_log==NO) {20.20// NSLog(@"该行代码只执行一次");21.21// _log=YES;22.22// }23.23//}24.2425.25-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event26.26{27.27staticdispatch_once_t onceToken;28.28dispatch_once(&onceToken, ^{29.29NSLog(@"该行代码只执行一次");30.30});31.31}32.32@end效果(程序运行过程中,打印代码只会执行一次):

三、队列组
需求:从网络上下载两张图片,把两张图片合并成一张最终显示在view上。
1.第一种方法
代码示例:
01.1//02.2// YYViewController.m03.3// 04-GCD基本使用(队列组下载图片)04.4//05.5// Created by apple on 14-6-25.06.6// Copyright (c) 2014年 itcase. All rights reserved.07.7//08.809.9#import"YYViewController.h"10.10//宏定义全局并发队列11.11#define global_quque dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)12.12//宏定义主队列13.13#define main_queue dispatch_get_main_queue()14.1415.15@interfaceYYViewController ()16.16@property(weak, nonatomic) IBOutlet UIImageView *imageView1;17.17@property(weak, nonatomic) IBOutlet UIImageView *imageView2;18.18@property(weak, nonatomic) IBOutlet UIImageView *imageView3;19.1920.20@end21.2122.22@implementationYYViewController23.2324.24- (void)viewDidLoad25.25{26.26[superviewDidLoad];27.27}28.28-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event29.29{30.30//获取全局并发队列31.31// dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);32.32//获取主队列33.33// dispatch_queue_t queue= dispatch_get_main_queue();34.3437.37dispatch_async(global_quque, ^{38.38//下载图片139.39UIImage *image1= [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];40.40NSLog(@"图片1下载完成---%@",[NSThread currentThread]);41.4142.42//下载图片243.43UIImage *image2= [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];44.44NSLog(@"图片2下载完成---%@",[NSThread currentThread]);45.4546.46//回到主线程显示图片47.47dispatch_async(main_queue, ^{48.48NSLog(@"显示图片---%@",[NSThread currentThread]);49.49self.imageView1.image=image1;50.50self.imageView2.image=image2;51.51//合并两张图片52.52UIGraphicsBeginImageContextWithOptions(CGSizeMake(200,100), NO,0.0);53.53[image1 drawInRect:CGRectMake(0,0,100,100)];54.54[image2 drawInRect:CGRectMake(100,0,100,100)];55.55self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();56.56//关闭上下文57.57UIGraphicsEndImageContext();58.58NSLog(@"图片合并完成---%@",[NSThread currentThread]);59.59});60.60//61.61});62.62}63.6364.64//封装一个方法,传入一个url参数,返回一张网络上下载的图片65.65-(UIImage *)imageWithUrl:(NSString *)urlStr66.66{67.67NSURL *url=[NSURL URLWithString:urlStr];68.68NSData *data=[NSData dataWithContentsOfURL:url];69.69UIImage *image=[UIImage imageWithData:data];70.70returnimage;71.71}72.72@end显示效果:

打印查看:

问题:这种方式的效率不高,需要等到图片1.图片2都下载完成后才行。
提示:使用队列组可以让图片1和图片2的下载任务同时进行,且当两个下载任务都完成的时候回到主线程进行显示。
2.使用队列组解决
步骤:
创建一个组
开启一个任务下载图片1
开启一个任务下载图片2
同时执行下载图片1下载图片2操作
等group中的所有任务都执行完毕, 再回到主线程执行其他操作
代码示例
001.1//002.2// YYViewController.m003.3// 04-GCD基本使用(队列组下载图片)004.4//005.5// Created by apple on 14-6-25.006.6// Copyright (c) 2014年 itcase. All rights reserved.007.7//008.8009.9#import"YYViewController.h"010.10//宏定义全局并发队列011.11#define global_quque dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)012.12//宏定义主队列013.13#define main_queue dispatch_get_main_queue()014.14015.15@interfaceYYViewController ()016.16@property(weak, nonatomic) IBOutlet UIImageView *imageView1;017.17@property(weak, nonatomic) IBOutlet UIImageView *imageView2;018.18@property(weak, nonatomic) IBOutlet UIImageView *imageView3;019.19020.20@end021.21022.22@implementationYYViewController023.23024.24- (void)viewDidLoad025.25{026.26[superviewDidLoad];027.27}028.28-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event029.29{032.32033.33034.34//1.创建一个队列组035.35dispatch_group_t group = dispatch_group_create();036.36037.37//2.开启一个任务下载图片1038.38__block UIImage *image1=nil;039.39dispatch_group_async(group, global_quque, ^{040.40image1= [self imageWithUrl:@"http://d.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=2b9a12172df5e0fefa1581533d095fcd/cefc1e178a82b9019115de3d738da9773912ef00.jpg"];041.41NSLog(@"图片1下载完成---%@",[NSThread currentThread]);042.42});043.43044.44//3.开启一个任务下载图片2045.45__block UIImage *image2=nil;046.46dispatch_group_async(group, global_quque, ^{047.47image2= [self imageWithUrl:@"http://h.hiphotos.baidu.com/baike/c0%3Dbaike80%2C5%2C5%2C80%2C26/sign=f47fd63ca41ea8d39e2f7c56f6635b2b/1e30e924b899a9018b8d3ab11f950a7b0308f5f9.jpg"];048.48NSLog(@"图片2下载完成---%@",[NSThread currentThread]);049.49});050.50051.51//同时执行下载图片1下载图片2操作052.52053.53//4.等group中的所有任务都执行完毕, 再回到主线程执行其他操作054.54dispatch_group_notify(group,main_queue, ^{055.55NSLog(@"显示图片---%@",[NSThread currentThread]);056.56self.imageView1.image=image1;057.57self.imageView2.image=image2;058.58059.59//合并两张图片060.60//注意最后一个参数是浮点数(0.0),不要写成0。061.61UIGraphicsBeginImageContextWithOptions(CGSizeMake(200,100), NO,0.0);062.62[image1 drawInRect:CGRectMake(0,0,100,100)];063.63[image2 drawInRect:CGRectMake(100,0,100,100)];064.64self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();065.65//关闭上下文066.66UIGraphicsEndImageContext();067.67068.68NSLog(@"图片合并完成---%@",[NSThread currentThread]);069.69});070.70071.71}072.72-(void)download2image073.73{074.74//获取全局并发队列075.75// dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);076.76//获取主队列077.77// dispatch_queue_t queue= dispatch_get_main_queue();078.78079.79dispatch_async(global_quque, ^{080.80//下载图片1081.81UIImage *image1= [self imageWithUrl:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];082.82NSLog(@"图片1下载完成---%@",[NSThread currentThread]);083.83084.84//下载图片2085.85UIImage *image2= [self imageWithUrl:@"http://news.baidu.com/z/resource/r/image/2014-06-22/2a1009253cf9fc7c97893a4f0fe3a7b1.jpg"];086.86NSLog(@"图片2下载完成---%@",[NSThread currentThread]);087.87088.88//回到主线程显示图片089.89dispatch_async(main_queue, ^{090.90NSLog(@"显示图片---%@",[NSThread currentThread]);091.91self.imageView1.image=image1;092.92self.imageView2.image=image2;093.93//合并两张图片094.94UIGraphicsBeginImageContextWithOptions(CGSizeMake(200,100), NO,0.0);095.95[image1 drawInRect:CGRectMake(0,0,100,100)];096.96[image2 drawInRect:CGRectMake(0,0,100,100)];097.97self.imageView3.image=UIGraphicsGetImageFromCurrentImageContext();098.98//关闭上下文099.99UIGraphicsEndImageContext();100.100NSLog(@"图片合并完成---%@",[NSThread currentThread]);101.101});102.102//103.103});104.104}105.105106.106//封装一个方法,传入一个url参数,返回一张网络上下载的图片107.107-(UIImage *)imageWithUrl:(NSString *)urlStr108.108{109.109NSURL *url=[NSURL URLWithString:urlStr];110.110NSData *data=[NSData dataWithContentsOfURL:url];111.111UIImage *image=[UIImage imageWithData:data];112.112returnimage;113.113}114.114@end打印查看(同时开启了两个子线程,分别下载图片):

2.补充说明
有这么1种需求:
首先:分别异步执行2个耗时的操作
其次:等2个异步操作都执行完毕后,再回到主线程执行操作
如果想要快速高效地实现上述需求,可以考虑用队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});