- GCD串行队列与并发队列
- GCD延时执行
- GCD线程组
- GCD定时器
- GCD信号量
- GCD综合使用示例
- GCD串行队列与并发队列:
串行队列一次只执行一个线程,按照添加到队列的顺序依次执行。
并发队列一次可以执行多个线程,线程的执行没有先后顺序。
UI界面所在的线程队列是串行队列:即业务逻辑在子线程中处理,界面刷新回到主线程 - 并发队列
* 并发队列
*/
- (void)initConcurrent
{
// 创建串行队列
GCDQueue *queue = [[GCDQueue alloc] initConcurrent];
// 执行队列中的线程
[queue execute:^{
NSLog(@"1");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"2");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"3");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"4");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"5");
}];
}
- 打印结果:
按顺序执行
- 并发队列
/**
* 并发队列
*/
- (void)initConcurrent
{
// 创建串行队列
GCDQueue *queue = [[GCDQueue alloc] initConcurrent];
// 执行队列中的线程
[queue execute:^{
NSLog(@"1");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"2");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"3");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"4");
}];
// 执行队列中的线程
[queue execute:^{
NSLog(@"5");
}];
}
- 打印结果:
无顺序执行
- 子线程处理业务逻辑,回到主线程刷新UI
- (void)viewDidLoad {
[super viewDidLoad];
// [self serailQueue];
[self initConcurrent];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
self.imageView.center = self.view.center;
[self.view addSubview:self.imageView];
[GCDQueue executeInGlobalQueue:^{
// 子线程中处理业务逻辑
NSString *pictureUrl = @"http://upload-images.jianshu.io/upload_images/189984-89d7604ddd8f90b5.png";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:pictureUrl]];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
self.image = [UIImage imageWithData:data];
NSLog(@"处理业务逻辑");
[GCDQueue executeInMainQueue:^{
// 主线程中更新UI
self.imageView.image = self.image;
NSLog(@"更新UI");
}];
}];
}
- GCD延时执行
// GCD方式的延时执行操作
[GCDQueue executeInMainQueue:^{
NSLog(@"GCD延时线程事件");
} afterDelaySecs:2.f];
- 与NSThread方法的延时执行进行比较
// NSThread方式延迟操作
[self performSelector:@selector(threadEvent:) withObject:self afterDelay:2.f];
- 取消
// 取消延时执行操作
[NSObject cancelPreviousPerformRequestsWithTarget:self];
比较结果:
- GCD延时执行的优缺点
优点:代码简洁
缺点:精确度低,不能取消 - GCD 线程组
- GCD 线程组的用处
- GCD 线程组进行事件监听
- GCD 线程组使用的场景
- (void)viewDidLoad {
[super viewDidLoad];
// 等待 线程一 与 线程二 执行完毕再去执行线程三
// GCD线程组
// 初始化线程组
GCDGroup *group = [[GCDGroup alloc] init];
// 初始化队列
GCDQueue *queue = [[GCDQueue alloc] init];
// 添加事件
[queue execute:^{
sleep(1); // 休眠一秒
NSLog(@"线程一事件完毕");
} inGroup:group];
[queue execute:^{
sleep(3); // 休眠三秒执行
NSLog(@"线程二事件执行完毕");
} inGroup:group];
// 监听事件一和时间二执行完毕之后执行事件三
[queue notify:^{
NSLog(@"线程三事件执行完毕");
} inGroup:group];
}
- 运行结果:
- GCD定时器
- 构建并使用GCD定时器
/**
* GCD定时器
*/
- (void)runGCDTimer
{
// 初始化定时器
self.gcdTimer = [[GCDTimer alloc] initInQueue:[GCDQueue mainQueue]];
// 指定时间间隔以及要执行的事件
[self.gcdTimer event:^{
NSLog(@"GCD定时器");
} timeInterval:NSEC_PER_SEC]; // 一秒执行
// 运行GCD定时器
[self.gcdTimer start];
}
- NSTimer定时器
/**
* NSTimer定时器
*/
- (void)runNormalTimer
{
self.normalTimer = [NSTimer scheduledTimerWithTimeInterval:1.f target:self selector:@selector(runNSTimer) userInfo:nil repeats:YES];
}
- (void)runNSTimer
{
NSLog(@"运行NSTimer定时器");
}
- GCD定时器和NSTimer定时器的一些区别
区别:NSTimer是运行在runloop中的,如果在TableView中使用了NSTimer定时器,会出现一些奇怪的问题,而使用GCD定时器,则不会存在这些问题 - GCD信号量
- 构建并使用GCD信号量
- 用GCD信号量将异步线程转化为同步线程
- (void)viewDidLoad {
[super viewDidLoad];
// // 创建信号量
GCDSemaphore *semaphore = [[GCDSemaphore alloc] init];
// // 发送信号
// [semaphore signal];
// // 等待信号(注意:发送信号和等待信号是同步使用的)
// [semaphore wait];
// 线程1 - 异步
[GCDQueue executeInGlobalQueue:^{
NSLog(@"1");
[semaphore signal]; // 执行完毕,发送信号
}];
// 线程2 - 异步
[GCDQueue executeInGlobalQueue:^{
[semaphore wait]; // 等待信号,收到之后再执行
NSLog(@"2");
}];
// 必须先执行完线程一,再执行线程二
}
@end
- 运行结果
有了信号量的检测,我们发现只有在线程一执行完毕才会执行线程二,这种情况在真实项目中开发不多见,但是极端的情况下就会发现信号量是解决异步线程先后顺序的利器!
- GCD综合使用示例
- 用并发下载多张图片来演示GCD的强大之处
- 异步下载三张图片
#import "ViewController.h"
#import "GCD.h"
@interface ViewController ()
@property (nonatomic, strong) UIImageView *view1;
@property (nonatomic, strong) UIImageView *view2;
@property(nonatomic, strong) UIImageView * view3;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化三个imageView用来显示图片
self.view1 = [self createImageViewWithFrame:CGRectMake(0, 0, 100, 100)];
self.view2 = [self createImageViewWithFrame:CGRectMake(100, 0, 100, 100)];
self.view3 = [self createImageViewWithFrame:CGRectMake(200, 0, 100, 100)];
// 初始化三个网址
NSString *net1 = @"http://ww2.sinaimg.cn/large/68d8771bgw1eupg6yqzzvj20m80etgo5.jpg";
NSString *net2 = @"http://ww4.sinaimg.cn/large/68d8771bgw1eupg7b3r92j20eu0m7n07.jpg";
NSString *net3 = @"http://ww3.sinaimg.cn/large/68d8771bgw1eupg7oceo7j20cy0ihn27.jpg";
// 初始化信号量
GCDSemaphore *semaphore = [[GCDSemaphore alloc] init];
// 图片下载
[GCDQueue executeInGlobalQueue:^{
UIImage *image1 = [self accessDataImageByNetString:net1];
[GCDQueue executeInMainQueue:^{
// 动画
[UIView animateWithDuration:2.f animations:^{
self.view1.image = image1;
self.view1.alpha = 1.f;
} completion:^(BOOL finished) {
// 第一张图片完全显示完毕发送信号
[semaphore signal];
}];
}];
}];
[GCDQueue executeInGlobalQueue:^{
UIImage *image2 = [self accessDataImageByNetString:net2];
// 阻塞线程,等待第一张图片的下载完毕执行动画
[semaphore wait];
[GCDQueue executeInMainQueue:^{
[UIView animateWithDuration:2.f animations:^{
self.view2.image = image2;
self.view2.alpha = 1.f;
} completion:^(BOOL finished) {
[semaphore signal];
}];
}];
}];
[GCDQueue executeInGlobalQueue:^{
UIImage *image3 = [self accessDataImageByNetString:net3];
// 阻塞线程,等待第二张图片下载完毕执行动画
[semaphore wait];
[GCDQueue executeInMainQueue:^{
[UIView animateWithDuration:2.f animations:^{
self.view3.image = image3;
self.view3.alpha = 1.f;
}];
}];
}];
}
/**
* 获取网络图片
*
* @param netString 图片网址
*
* @return 图片
*/
- (UIImage *)accessDataImageByNetString:(NSString *)netString
{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:netString]];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
UIImage *image = [[UIImage alloc] initWithData:data];
return image;
}
/**
* 创建ImageView
*
* @param frame frame
*
* @return imageView
*/
- (UIImageView *)createImageViewWithFrame:(CGRect)frame
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.alpha = 0.f;
[self.view addSubview:imageView];
return imageView;
}
@end
- 运行结果
- 用线程组实现
GCDGroup *group = [[GCDGroup alloc] init];
GCDQueue *queue = [[GCDQueue alloc] init];
[queue execute:^{
UIImage *image1 = [self accessDataImageByNetString:net1];
[GCDQueue executeInMainQueue:^{
[UIView animateWithDuration:2.f animations:^{
self.view1.image = image1;
self.view1.alpha = 1.f;
} completion:^(BOOL finished) {
}];
}];
} inGroup:group];
[queue execute:^{
UIImage *image2 = [self accessDataImageByNetString:net2];
[GCDQueue executeInMainQueue:^{
[UIView animateWithDuration:2.f animations:^{
self.view2.image = image2;
self.view2.alpha = 1.f;
} completion:^(BOOL finished) {
}];
}];
} inGroup:group];
[queue notify:^{
UIImage *image3 = [self accessDataImageByNetString:net3];
[GCDQueue executeInMainQueue:^{
[UIView animateWithDuration:2.f animations:^{
self.view3.image = image3;
self.view3.alpha = 1.f;
} completion:^(BOOL finished) {
}];
}];
} inGroup:group];
- 掌握
- GCD串行队列与并发队列
- GCD延时执行操作
- GCD线程组的使用
- GCD定时器的使用
- 用GCD信号量将异步操作转化为同步操作
关于作者