---恢复内容开始---
GCD
GCD的优势
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动将队列中的任务取出,放到对应的线程中执行
- 任务的取出遵循队列的FIFO原则:先进先出,后进后出
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
/********************************创建串行queue队列********************************/
DISPATCH_QUEUE_SERIAL这里换成NULL也是创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
/********************************第一次向queue添加任务********************************/
for (int i = 0; i<3; i++) {
NSLog(@"A------%@", [NSThread currentThread]);
}
for (int i = 0; i<3; i++) {
NSLog(@"B------%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
/********************************第二次向queue添加任务********************************/
for (int i = 0; i<3; i++) {
NSLog(@"C------%@", [NSThread currentThread]);
}
for (int i = 0; i<3; i++) {
NSLog(@"D------%@", [NSThread currentThread]);
}
});
dispatch_release(queue);
NSLog(@"E--------%@", [NSThread currentThread]);
}
输出结果:
2016-03-29 21:28:50.991 test1[1021:62256] E--------<NSThread: 0x7fcaa0601a00>{number = 1, name = main}
2016-03-29 21:28:50.991 test1[1021:62291] A------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.992 test1[1021:62291] A------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.992 test1[1021:62291] A------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.992 test1[1021:62291] B------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] B------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] B------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] C------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] C------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] C------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.994 test1[1021:62291] D------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.994 test1[1021:62291] D------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.994 test1[1021:62291] D------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
上面两句话结合代码分析:就是当我们把我们要做的事情(任务)添加进一个队列中,可能我们分多次添加,上面的代码中我是跟两次在queue队列中添加任务,当GCD把queue队列中的任务取出放到对应的线程中执行的时候, 它会遵守FIFO(先进先出的原则),先执行第一次添加的任务(输出A B),然后执行第二次添加的任务(输出C D),为什么输出结果中会首先输出E呢? 因为这里执行的异步函数执行任务,不会阻塞线程 (下面会讲异步函数) 串行队列 + 异步执行函数会开启一条子线程
注意:上面的案例是在MRC中,所以通过creat创建出来的队列,需要release!
/********************************创建串行queue队列********************************/
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
/********************************创建并发queue队列********************************/
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
串行队列:队列中的任务执行的方式:任务一个接着一个的执行(一个执行完毕以后,在执行下一个任务)
并发队列:当GCD取出队列中的任务的时候,执行的方式:如果是异步执行的方式的执行下,自动开启多个线程同时执行任务)
将上面的案例中的队列更改成并发队列,我们分析下结果:
/********************************创建并发queue队列********************************/
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
/********************************第一次向queue添加任务********************************/
for (int i = 0; i<3; i++) {
NSLog(@"A------%@", [NSThread currentThread]);
}
for (int i = 0; i<3; i++) {
NSLog(@"B------%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
/********************************第二次向queue添加任务********************************/
for (int i = 0; i<3; i++) {
NSLog(@"C------%@", [NSThread currentThread]);
}
for (int i = 0; i<3; i++) {
NSLog(@"D------%@", [NSThread currentThread]);
}
});
dispatch_release(queue);
NSLog(@"E--------%@", [NSThread currentThread]);
输出结果:
2016-03-29 21:48:37.056 test1[1043:68365] E--------<NSThread: 0x7fd838402780>{number = 1, name = main} 2016-03-29 21:48:37.056 test1[1043:68403] C------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)} 2016-03-29 21:48:37.056 test1[1043:68404] A------<NSThread: 0x7fd83843b470>{number = 3, name = (null)} 2016-03-29 21:48:37.056 test1[1043:68404] A------<NSThread: 0x7fd83843b470>{number = 3, name = (null)} 2016-03-29 21:48:37.056 test1[1043:68403] C------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)} 2016-03-29 21:48:37.057 test1[1043:68404] A------<NSThread: 0x7fd83843b470>{number = 3, name = (null)} 2016-03-29 21:48:37.057 test1[1043:68403] C------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)} 2016-03-29 21:48:37.057 test1[1043:68404] B------<NSThread: 0x7fd83843b470>{number = 3, name = (null)} 2016-03-29 21:48:37.057 test1[1043:68403] D------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)} 2016-03-29 21:48:37.057 test1[1043:68404] B------<NSThread: 0x7fd83843b470>{number = 3, name = (null)} 2016-03-29 21:48:37.057 test1[1043:68403] D------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)} 2016-03-29 21:48:37.058 test1[1043:68404] B------<NSThread: 0x7fd83843b470>{number = 3, name = (null)} 2016-03-29 21:48:37.058 test1[1043:68403] D------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)}
结果分析:第一行依然输出的是E;因为我们执行任务的方式是异步函数,所以不会堵塞线程,输出的是E,但是这次由于我们创建的队列是并发的,当GCD从队列中取出任务的时候,会自动创建多个线程(根据CPU资源创建),不仅从输出的结果可以看出,创建多条线程,并且执行的任务的时候没有顺序的,说明是并发执行任务的
下面我们看一下同步函数执行任务 + 串行队列 或者 并发队列是什么结果呢
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
/********************************创建异步queue队列********************************/
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
/********************************第一次向queue添加任务********************************/
for (int i = 0; i<3; i++) {
NSLog(@"A------%@", [NSThread currentThread]);
}
for (int i = 0; i<3; i++) {
NSLog(@"B------%@", [NSThread currentThread]);
}
});
/********************************同步方式执行********************************/
dispatch_sync(queue, ^{
/********************************第二次向queue添加任务********************************/
for (int i = 0; i<3; i++) {
NSLog(@"C------%@", [NSThread currentThread]);
}
for (int i = 0; i<3; i++) {
NSLog(@"D------%@", [NSThread currentThread]);
}
});
dispatch_release(queue);
NSLog(@"E--------%@", [NSThread currentThread]);
}
输出结果
2016-03-29 22:04:22.972 test1[1122:74978] A------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.972 test1[1122:74978] A------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] A------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] B------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] B------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] B------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] C------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] C------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] C------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] D------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] D------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] D------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] E--------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
输出的结果:当执行任务的方式采用的是同步执行的时候,不管是串行队列,还是并发队列,都不会开启子线程<也就是说,并发队列只在异步函数执行任务中会有效(创建子线程), E输出在最后输出,整体发现,任务是在主线程执行,并且任务是按着循序执行的,也就是说同步函数会阻塞线程!
全局队列
全局并发队列
GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建
/** * <#Description#> * * @param identifier#> 队列的优先级 一般也传0 * @param flags#> 此参数暂时无用,用0即可 * 全局并发队列的优先级 #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高 #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中) #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低 #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台 */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
全局主队列
dispatch_queue_t queue = dispatch_get_main_queue();
线程之间通信
从子线程回到主线程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 执行耗时的异步操作...
dispatch_async(dispatch_get_main_queue(), ^{
// 回到主线程,执行UI刷新操作
});
});
在开发中GCD的其他常用API接口
延时执行
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ [self performSelector:@selector(task) withObject:nil afterDelay:2.0]; } - (void)task { NSLog(@"延迟2秒执行---%@", [NSThread currentThread]); }
输出结果
2016-03-29 22:29:43.308 test1[1153:84643] 延迟2秒执行---<NSThread: 0x7fa682e005e0>{number = 1, name = main}
结果分析:延迟两秒执行task方法,在task所在的线程是主线程
一次性代码
使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
在整个程序中,只会执行一次,开发中可用于单例
单例模式
ARC中,单例模式的实现 在.m中保留一个全局的static的实例 static id _instance; 重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全) + (instancetype)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance; }
提供1个类方法让外界访问唯一的实例 + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [[self alloc] init]; }); return _instance; } 实现copyWithZone:方法 - (id)copyWithZone:(struct _NSZone *)zone { return _instance; }
快速迭代
// 使用dispatch_apply函数能进行快速迭代遍历 NSArray *array = @[@"coder_hong", @"zhangsan", @"lishi"]; NSUInteger count = array.count; dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index){ // 执行10次代码,index顺序不确定 NSLog(@"%@", array[index]); });
输出结果:
2016-03-29 22:45:47.886 test1[1228:91621] coder_hong
2016-03-29 22:45:47.886 test1[1228:91661] lishi
2016-03-29 22:45:47.886 test1[1228:91657] zhangsan