GCD常用基本API介绍以及创建单例的两种方式
2016-12-21 14:17
531 查看
GDC的介绍有另外一篇博客专门写了下,需要看的可以点击以下传送门GCD线程死锁解锁案例分析,这里主要记录下基本的API使用以及两种单例的创建方式
可以看出同步和异步就是开线程的能力,同步执行必然一个个顺序执行在当前线程,而异步执行可以根据队列不同来确定顺序还是同步并发执行
dispatch_apply
dispatch_after
dispatch_group_async
GCD实现(直接抽成宏)
抽成宏之后可以直接进行调用就OK
注:这里为什么需要用Static进行修饰,static保证只有本文件才能访问,这样就能形成单例,但是如果不用static修饰,外部也一样能访问,这个时候外部如果置为nil之后,这个对象就不存在了,再也不会创建了,dispatch_once只会执行一次,这样肯定不对的
非常简单的介绍,这里有个Demo,需要的朋友可以下载看看基础用法先,文章头部有介绍GCD死锁解锁的案例文章,需要的也可以看看
本文Demo传送门
NSOperation用法和GCD之间的关系
1.GCD同步异步并发串行排列组合的集中基本形式
/* * 异步串行 ---> 不阻塞当前线程,会开多一条线程,在这个线程中是串行执行的 */ - (void)asynchSerial { // 全局并发队列 // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 自定义并发队列 dispatch_queue_t queue1 = dispatch_queue_create("COM.MKJ.COM", DISPATCH_QUEUE_SERIAL); dispatch_async(queue1, ^{ NSLog(@"------1,%@",[NSThread currentThread]); }); dispatch_async(queue1, ^{ NSLog(@"------2,%@",[NSThread currentThread]); }); dispatch_async(queue1, ^{ NSLog(@"------3,%@",[NSThread currentThread]); }); NSLog(@"asynchSerial------end"); // 2016-12-21 09:34:16.222 GCDBasicAPI[1137:38266] asynchSerial------end // 2016-12-21 09:34:16.222 GCDBasicAPI[1137:38300] ------1,<NSThread: 0x60000007a640>{number = 3, name = (null)} // 2016-12-21 09:34:16.223 GCDBasicAPI[1137:38300] ------2,<NSThread: 0x60000007a640>{number = 3, name = (null)} // 2016-12-21 09:34:16.223 GCDBasicAPI[1137:38300] ------3,<NSThread: 0x60000007a640>{number = 3, name = (null)} } /* * 同步串行 ---> 阻塞当前线程,不开多条线程,在当前线程执行,顺序执行 */ - (void)synchSerial { // 全局并发队列 // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 自定义并发队列 dispatch_queue_t queue1 = dispatch_queue_create("COM.MKJ.COM", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue1, ^{ NSLog(@"------1,%@",[NSThread currentThread]); }); dispatch_sync(queue1, ^{ NSLog(@"------2,%@",[NSThread currentThread]); }); dispatch_sync(queue1, ^{ NSLog(@"------3,%@",[NSThread currentThread]); }); NSLog(@"synchSerial------end"); // 2016-12-21 09:32:25.878 GCDBasicAPI[1117:36348] ------1,<NSThread: 0x600000075700>{number = 1, name = main} // 2016-12-21 09:32:25.878 GCDBasicAPI[1117:36348] ------2,<NSThread: 0x600000075700>{number = 1, name = main} // 2016-12-21 09:32:25.879 GCDBasicAPI[1117:36348] ------3,<NSThread: 0x600000075700>{number = 1, name = main} // 2016-12-21 09:32:25.879 GCDBasicAPI[1117:36348] synchSerial------end } /* * 同步并发 ---> 阻塞当前线程,不开多条线程,在当前线程执行,顺序执行 */ - (void)synchConcurrent { // 全局并发队列 // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 自定义并发队列 dispatch_queue_t queue1 = dispatch_queue_create("COM.MKJ.COM", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue1, ^{ NSLog(@"------1,%@",[NSThread currentThread]); }); dispatch_sync(queue1, ^{ NSLog(@"------2,%@",[NSThread currentThread]); }); dispatch_sync(queue1, ^{ NSLog(@"------3,%@",[NSThread currentThread]); }); NSLog(@"synchConcurrent------end"); // 2016-12-21 09:29:47.575 GCDBasicAPI[1071:33607] ------1,<NSThread: 0x60000006b900>{number = 1, name = main} // 2016-12-21 09:29:47.575 GCDBasicAPI[1071:33607] ------2,<NSThread: 0x60000006b900>{number = 1, name = main} // 2016-12-21 09:29:47.576 GCDBasicAPI[1071:33607] ------3,<NSThread: 0x60000006b900>{number = 1, name = main} // 2016-12-21 09:29:47.576 GCDBasicAPI[1071:33607] synchConcurrent------end } /* * 异步并发 ---> 不阻塞当前线程,可以开多条线程,并发执行,顺序不一定 */ - (void)asynchConcurrent { // 全局并发队列 // dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 自定义并发队列 dispatch_queue_t queue1 = dispatch_queue_create("COM.MKJ.COM", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue1, ^{ NSLog(@"------1,%@",[NSThread currentThread]); }); dispatch_async(queue1, ^{ NSLog(@"------2,%@",[NSThread currentThread]); }); dispatch_async(queue1, ^{ NSLog(@"------3,%@",[NSThread currentThread]); }); NSLog(@"asynchConcurrent------end"); // 2016-12-21 09:28:23.895 GCDBasicAPI[1050:32223] asynchConcurrent------end // 2016-12-21 09:28:23.895 GCDBasicAPI[1050:32292] ------1,<NSThread: 0x60800026e180>{number = 3, name = (null)} // 2016-12-21 09:28:23.895 GCDBasicAPI[1050:32293] ------3,<NSThread: 0x60800026fbc0>{number = 5, name = (null)} // 2016-12-21 09:28:23.895 GCDBasicAPI[1050:32295] ------2,<NSThread: 0x60000026e0c0>{number = 4, name = (null)} }
简单概括如下:
项目 | 同步(sync) | 异步(async) |
---|---|---|
串行 | 当前线程,顺序执行 | 另一个线程,顺序执行 |
并发 | 当前线程,顺序执行 | 另一个线程,同时执行 |
2.最基本的线程间通讯方式
// 不阻塞当前线程,后台拉取图片资源 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSURL *url = [NSURL URLWithString:@"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcRCfHVwGXGvrpCBplQieSKsLgfBULL8ZZXSzosPFdoZsvjDlqnOrKK_w58"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; NSLog(@"获取资源%@",[NSThread currentThread]); // 下载完之后回到主线程 dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; NSLog(@"加载资源%@",[NSThread currentThread]); }); }); // 2016-12-21 09:43:51.675 GCDBasicAPI[1237:45972] 获取资源<NSThread: 0x6000002742c0>{number = 3, name = (null)} // 2016-12-21 09:43:51.676 GCDBasicAPI[1237:45929] 加载资源<NSThread: 0x60000007b680>{number = 1, name = main}
3.其他Barrier,apply,after,Group等常用API
dispatch_barrier_async/* * barrier函数可以阻塞任务,执行到他这里,要等之前的任务执行完才能执行之后的任务 */ - (void)barrier { dispatch_queue_t queue = dispatch_queue_create("com.mkj.hehe", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"--------1,%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ for (NSInteger i = 0; i < 10; i ++) { NSLog(@"--------2%ld,%@",i,[NSThread currentThread]); } }); dispatch_barrier_async(queue, ^{ NSLog(@"--------3,%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------4,%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------5,%@",[NSThread currentThread]); }); // 2016-12-21 09:58:09.231 GCDBasicAPI[1392:57437] --------1,<NSThread: 0x60000007f400>{number = 5, name = (null)} // 2016-12-21 09:58:09.231 GCDBasicAPI[1392:57807] --------20,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.232 GCDBasicAPI[1392:57807] --------21,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.233 GCDBasicAPI[1392:57807] --------22,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.233 GCDBasicAPI[1392:57807] --------23,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.233 GCDBasicAPI[1392:57807] --------24,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.233 GCDBasicAPI[1392:57807] --------25,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.233 GCDBasicAPI[1392:57807] --------26,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.234 GCDBasicAPI[1392:57807] --------27,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.234 GCDBasicAPI[1392:57807] --------28,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.234 GCDBasicAPI[1392:57807] --------29,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.234 GCDBasicAPI[1392:57807] --------3,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.235 GCDBasicAPI[1392:57807] --------4,<NSThread: 0x60000007e780>{number = 7, name = (null)} // 2016-12-21 09:58:09.235 GCDBasicAPI[1392:57437] --------5,<NSThread: 0x60000007f400>{number = 5, name = (null)} }
dispatch_apply
/* * apply 无序快速遍历,可以用于文件移动等需求 */ - (void)apply { dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) { NSLog(@"%ld,%@",index,[NSThread currentThread]); }); // 2016-12-21 10:04:01.487 GCDBasicAPI[1485:63041] 0,<NSThread: 0x600000069940>{number = 1, name = main} // 2016-12-21 10:04:01.487 GCDBasicAPI[1485:63172] 2,<NSThread: 0x60800007a940>{number = 7, name = (null)} // 2016-12-21 10:04:01.487 GCDBasicAPI[1485:63845] 1,<NSThread: 0x60800007a8c0>{number = 6, name = (null)} // 2016-12-21 10:04:01.487 GCDBasicAPI[1485:63873] 3,<NSThread: 0x60800007ab00>{number = 8, name = (null)} // 2016-12-21 10:04:01.487 GCDBasicAPI[1485:63041] 4,<NSThread: 0x600000069940>{number = 1, name = main} // 2016-12-21 10:04:01.488 GCDBasicAPI[1485:63172] 5,<NSThread: 0x60800007a940>{number = 7, name = (null)} // 2016-12-21 10:04:01.488 GCDBasicAPI[1485:63845] 6,<NSThread: 0x60800007a8c0>{number = 6, name = (null)} // 2016-12-21 10:04:01.488 GCDBasicAPI[1485:63873] 7,<NSThread: 0x60800007ab00>{number = 8, name = (null)} // 2016-12-21 10:04:01.488 GCDBasicAPI[1485:63041] 8,<NSThread: 0x600000069940>{number = 1, name = main} // 2016-12-21 10:04:01.488 GCDBasicAPI[1485:63172] 9,<NSThread: 0x60800007a940>{number = 7, name = (null)} }
dispatch_after
/* * 延时执行 */ - (void)delay { // 方法1 NSLog(@"开始了"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"我才开始"); }); // 方法2 [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO]; } - (void)run { NSLog(@"run"); }
dispatch_group_async
/* * 线程组 实现多个请求完成之后同时刷新UI的问题 * 把多个任务加入到队列里面,最后都完成之后会调用Notify的方法进行通知,最终刷新UI */ - (void)group { // 并发队列 dispatch_queue_t queue = dispatch_queue_create("com.mkj.hh", DISPATCH_QUEUE_CONCURRENT); // 线程组 dispatch_group_t group = dispatch_group_create(); // 任务1加入组 dispatch_group_async(group, queue, ^{ NSURL *url = [NSURL URLWithString:@"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcQxex7CvJ0pArQ8NHwXMaZ8fSt3ALAZBlljTQVlDsh6AIegeMjWWMoSVtej"]; NSData *data = [NSData dataWithContentsOfURL:url]; self.image1 = [UIImage imageWithData:data]; }); // 任务2加入组 dispatch_group_async(group, queue, ^{ // 这里可以一样是耗时的网络请求,暂时处理成本地的 self.image2 = [UIImage imageNamed:@"Play"]; }); // 任务完成之后统一通知 dispatch_group_notify(group, queue, ^{ // 这里的queue如果是mainQueue的话就可以直接回到主线程操作需要的UI // 现在还是在并发队列里面 进行图片合成 还是放在子线程 UIGraphicsBeginImageContext(CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width)); [self.image1 drawInRect:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width)]; [self.image2 drawInRect:CGRectMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.width/2, 30, 40)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // 回到主线程刷新UI dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); }); }
4.两种方式实现单例
自己加锁实现static id obj; + (instancetype)shareInstance { @synchronized (self) { if (!obj) { obj = [[self alloc] init]; } } return obj; } + (instancetype)allocWithZone:(struct _NSZone *)zone { @synchronized (self) { if (!obj) { obj = [super allocWithZone:zone]; } } return obj; } - (id)copyWithZone:(NSZone *)zone { return obj; }
GCD实现(直接抽成宏)
#define MKJSingletonH + (instancetype)shareManager; #define MKJSinletonM \ static id obj; \ \ + (instancetype)shareManager \ { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ obj = [[self alloc] init]; \ }); \ return obj; \ } \ \ + (instancetype)allocWithZone:(struct _NSZone *)zone \ { \ static dispatch_once_t onceToken; \ dispatch_once(&onceToken, ^{ \ obj = [super allocWithZone:zone]; \ }); \ return obj; \ } \ \ - (id)copyWithZone:(NSZone *)zone \ { \ return obj; \ }
抽成宏之后可以直接进行调用就OK
@interface ManagerHelper : NSObject MKJSingletonH @end @implementation ManagerHelper MKJSinletonM @end
注:这里为什么需要用Static进行修饰,static保证只有本文件才能访问,这样就能形成单例,但是如果不用static修饰,外部也一样能访问,这个时候外部如果置为nil之后,这个对象就不存在了,再也不会创建了,dispatch_once只会执行一次,这样肯定不对的
非常简单的介绍,这里有个Demo,需要的朋友可以下载看看基础用法先,文章头部有介绍GCD死锁解锁的案例文章,需要的也可以看看
本文Demo传送门
NSOperation用法和GCD之间的关系
相关文章推荐
- 创建安卓模拟器的两种方式及常用Android命令介绍
- 创建单例的两种方式(懒汉式&GCD)
- 三种基本布局(不常用)以及全屏和Activity的两种启动方式
- 控件的创建方式、RelativeLayout基本属性、单个控件常用属性、官方API网址
- 动态链接库的创建以及两种调用方式
- day10 反射创建数组 线程的基本概念 线程的编写和启动方式 线程的运行状态以及状态转换方法 线程的调度和优先级设置
- 两种方式创建你自己的 Docker 基本映像
- VMM系列之VMM角色介绍以及创建运行方式账户
- API 基本文档 (基本常用,以及总结)
- iOS边练边学--GCD的基本使用、GCD各种队列、GCD线程间通信、GCD常用函数、GCD迭代以及GCD队列组
- Easyui 创建dialog的两种方式,以及他们带来的问题
- android(13)(sqlite的CRUD使用SQL和API两种方式实现以及Linearlayout的列表展示)
- ios多线程的几种创建方式以及基本使用
- 13 API-常用类(StringBuffer,数组高级以及Arrays,基本类型包装类{Integer,Character})
- spring 中常用的两种事务配置方式以及事务的传播性、隔离级别
- NSTimer 两种常用创建方式的区别
- 黑马程序员——常用API(StringBuilder和StringBuffer以及基本数据类型包装类)
- 线程Thread的两种创建方式以及区别
- java学习11--线程创建的两种方式,生命周期以及守护线程
- 两种方式创建你自己的 Docker 基本映像