美文网首页
iOS - GCD 用法详解

iOS - GCD 用法详解

作者: LYDeveloper | 来源:发表于2018-04-26 11:28 被阅读29次

    以前总是被并行、串行,同步、异步 队列搞得晕头转向,最近理顺了一遍GCD的用法,再也不迷路了。
    一、Dispatch Queue 是什么?
    执行处理的等待队列,简单点说就是:要做一件事情先给开条路给做好准备,做事情的时候直接处理事情。
    Dispatch Queue 按照追加的顺序(先进先出 FIFO)执行处理。
    二、 Dispatch Queue 队列种类
    1、Serial Dispatch Queue 等待现在执行中处理结束 等待执行 (串行)
    2、Concurent Dispatch Queue 不等待现在执行中处理结束 立即执行 (并行)
    三 、创建一个 Serial Dispatch Queue 队列,顺序执行任务。

     NSLog(@"begin");
        //    Serila Disapatch Queue 串行队列主要用在多个线程同事更新相同资源导致数据竞争时。
        //    第一个参数是进程的标识符 Apple 推荐写法为 使用应用程序ID这种逆序全程域名
        //    DISPATCH_QUEUE_SERIAL 表示队列类型为 串行队列  等待上个任务执行完成 再往下执行。
        dispatch_queue_t queue = dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);
        dispatch_async(queue, ^{
            NSLog(@"1");
        });
        dispatch_async(queue, ^{
            NSLog(@"2");
        });
        dispatch_async(queue, ^{
            NSLog(@"3");
        });
        dispatch_async(queue, ^{
            NSLog(@"4");
        });
        NSLog(@"end");
    

    执行顺序为:

    2018-04-26 10:45:33.304185+0800 GCD[1118:74823] begin
    2018-04-26 10:45:33.304396+0800 GCD[1118:74823] end
    2018-04-26 10:45:33.304408+0800 GCD[1118:74869] 1
    2018-04-26 10:45:33.304573+0800 GCD[1118:74869] 2
    2018-04-26 10:45:33.304710+0800 GCD[1118:74869] 3
    2018-04-26 10:45:33.305090+0800 GCD[1118:74869] 4
    

    结果很明显: 开始后新建了一个子线程queue, 主线程和子线程同时往下执行,主线程执行NSLog 输出@“end”,子线程按照顺序执行任务,输出 1 2 3 4 。

    四、创建一个 Concurrent Dispatch Queue 队列,并发执行任务。

         Concurrent Dispatch Queue  会立即执行1 ,不等1执行完 就开始执行2 ,不等2执行完就开始执行3,不等3执行完再回到
    
        NSLog(@"begin");
        //    并行  第一个参数 是线程标识符    第二个参数表示创建的是并行队列 立即执行
        dispatch_queue_t queue =dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"111111--- %d----%@", i, [NSThread currentThread]);
            };
        
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"222222--- %d----%@", i, [NSThread currentThread]);
            };
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"333333--- %d----%@", i, [NSThread currentThread]);
            };
        });
        NSLog(@"end");
    

    结果为:

    GCD[1489:125490] begin
    GCD[1489:125490] end
    GCD[1489:125581] 222222--- 0----<NSThread: 0x604000265480>{number = 4, name = (null)}
    GCD[1489:125580] 111111--- 0----<NSThread: 0x604000265400>{number = 3, name = (null)}
    GCD[1489:125578] 333333--- 0----<NSThread: 0x604000265580>{number = 5, name = (null)}
    GCD[1489:125581] 222222--- 1----<NSThread: 0x604000265480>{number = 4, name = (null)}
    GCD[1489:125580] 111111--- 1----<NSThread: 0x604000265400>{number = 3, name = (null)}
    GCD[1489:125578] 333333--- 1----<NSThread: 0x604000265580>{number = 5, name = (null)}
    GCD[1489:125581] 222222--- 2----<NSThread: 0x604000265480>{number = 4, name = (null)}
    GCD[1489:125580] 111111--- 2----<NSThread: 0x604000265400>{number = 3, name = (null)}
    GCD[1489:125578] 333333--- 2----<NSThread: 0x604000265580>{number = 5, name = (null)}
    

    Concurrent Dispatch Queue 急性子 不管你有没有做完前边的事情,我反正要立即执行,会开启多个线程完成这个事情。

    这是最基本的两种用法。

    刚刚是自己创建Queue ,其实我们也可以使用系统的Queue,

    五、系统Serial (串行)队列 dispatch_get_main_queue() 用法同三

     /**
         系统
         主队列
         是Serial 类型
         */
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
    

    六、系统Concurrent(并行) 队列 dispatch_get_global_queue(0, 0) 用法同四

       /**
         系统
         全局队列
         是Concurrent 类型
         第一个参数是优先级
         DISPATCH_QUEUE_PRIORITY_HIGH                  最高优先
         DISPATCH_QUEUE_PRIORITY_DEFAULT            默认优先级  0
         DISPATCH_QUEUE_PRIORITY_LOW                    低
         DISPATCH_QUEUE_PRIORITY_BACKGROUND   后台
         */
    
        dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    

    还有一些其他函数例如 :
    七、栅栏:dispatch_barrier_async()<用来控制队列的执行先完成barrier 前的任务在完成barrier后的任务>

    //    栅栏函数不能在 全局主线程中执行
    //    栅栏函数不能在 全局主线程中执行
        dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"11111%d-----%@", i,[NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"22222%d-----%@", i,[NSThread currentThread]);
            }
        });
            dispatch_barrier_async(queue, ^{
                NSLog(@"栅栏");
            });
        dispatch_async(queue, ^{
            for (int i = 0; i < 3; i++) {
                NSLog(@"33333%d-----%@", i,[NSThread currentThread]);
            }
        });
    

    输出结果为:

    GCD[2617:238165] 111110-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
    GCD[2617:238164] 222220-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
    GCD[2617:238165] 111111-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
    GCD[2617:238164] 222221-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
    GCD[2617:238165] 111112-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
    GCD[2617:238164] 222222-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
    GCD[2617:238164] 栅栏
    GCD[2617:238164] 333330-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
    GCD[2617:238164] 333331-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
    GCD[2617:238164] 333332-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
    

    八、稍等:dispatch_after() <在子线程中过一段时间执行某个任务>

     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"过2s后执行");
        });
    

    九、快速迭代:dispatch_apply() Demo 是from文件内容转移到to文件

     //    会阻塞主线程进行
        NSLog(@"begin");
        NSString *from = @"/Users/youName/Desktop/from";
        NSString *to = @"/Users/youName/Desktop/to";
        NSFileManager *fileManager = [NSFileManager defaultManager];
        NSArray *fileArray = [fileManager subpathsAtPath:from];
        /**
         第一个参数 :迭代次数
         第二个参数 :所在的queue
         第三个参数 :执行的任务
         */
        dispatch_apply([fileArray count], dispatch_queue_create("apply", DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
            sleep(2);
            NSLog(@"%zd ----%@",index, [NSThread currentThread]);
            NSString *fromPath = [from stringByAppendingPathComponent:fileArray[index]];
            NSString *toPath = [to stringByAppendingPathComponent:fileArray[index]];
            [fileManager moveItemAtPath:fromPath toPath:toPath error:nil];
        });
        NSLog(@"end");
    

    输出结果为:可以看到apply会阻塞主线程进行。

    2018-04-26 13:33:43.071251+0800 GCD[2735:248799] begin
    2018-04-26 13:33:45.073933+0800 GCD[2735:248896] 2 ----<NSThread: 0x600000275bc0>{number = 3, name = (null)}
    2018-04-26 13:33:45.073933+0800 GCD[2735:248799] 0 ----<NSThread: 0x6000000634c0>{number = 1, name = main}
    2018-04-26 13:33:45.073949+0800 GCD[2735:248895] 3 ----<NSThread: 0x604000263580>{number = 5, name = (null)}
    2018-04-26 13:33:45.074026+0800 GCD[2735:248898] 1 ----<NSThread: 0x6040002634c0>{number = 4, name = (null)}
    2018-04-26 13:33:47.077143+0800 GCD[2735:248799] 4 ----<NSThread: 0x6000000634c0>{number = 1, name = main}
    2018-04-26 13:33:47.078235+0800 GCD[2735:248799] end
    

    十、队列组 dispatch_group_t group = dispatch_group_create();<保证所有的任务都已经完成最后在执行某个任务>

        //    demo 是两张图片下载完成后再合成一张图片
        //    队列组  是保证任务都已经完成后在执行某个任务。
        dispatch_group_t group = dispatch_group_create(); //    创建队列组
        __block UIImage *image1 = [[UIImage alloc] init];
        __block  UIImage *image2 = [[UIImage alloc] init];
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
        imageView.contentMode = UIViewContentModeScaleAspectFill;
        [self.view addSubview:imageView];
        NSLog(@"begin");
        dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
            NSString *imagePath =@"http://h.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ecf70575602a9759ee3c6ddb99.jpg";
            image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
            NSLog(@"任务1完成");
        });
        dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
            NSString *imagePath =@"http://d.hiphotos.baidu.com/image/pic/item/8435e5dde71190ef3ddc94b7c21b9d16fdfa60b6.jpg";
            image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
            NSLog(@"任务2完成");
        });
        //    所有任务执行完成后才会执行这个任务。
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"所有任务执行完毕开始最后合成图片操作");
            //        拼接两张图片
            //        开启图形上下文
            UIGraphicsBeginImageContext(CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height));
            //        画1
            [image1 drawInRect:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width * 0.4)];
            //        画2
            [image2 drawInRect:CGRectMake(0, [UIScreen mainScreen].bounds.size.height *0.3, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.7)];
            //        得到绘制好的图片
            UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            //        关闭图形上下文
            //        回到主线程刷新UI
            NSLog(@"合成完毕");
            dispatch_async(dispatch_get_main_queue(), ^{
                imageView.image = image;
                NSLog(@"回到主线程刷新UI");
            });
        });
        
        NSLog(@"end");
    

    输出结果为:可以看到group会等任务都完成后再做最后的操作。

    2018-04-26 13:49:46.539181+0800 GCD[2910:273617] begin
    2018-04-26 13:49:46.539414+0800 GCD[2910:273617] end
    2018-04-26 13:49:48.905053+0800 GCD[2910:273683] 任务2完成
    2018-04-26 13:49:48.905053+0800 GCD[2910:273679] 任务1完成
    2018-04-26 13:49:48.905325+0800 GCD[2910:273617] 所有任务执行完毕开始最后合成图片操作
    2018-04-26 13:49:48.941813+0800 GCD[2910:273617] 合成完毕
    2018-04-26 13:49:48.942956+0800 GCD[2910:273617] 回到主线程刷新UI
    

    十一、单例 dispatch_once()

    //    在touchBegin 调用此方法
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"只会执行一次  适合全局单例对象使用");
        });
        NSLog(@"别点了,就会执行一次");
    

    输出结果为:

    2018-04-26 13:53:42.167590+0800 GCD[2974:280429] 只会执行一次  适合全局单例对象使用
    2018-04-26 13:53:42.167835+0800 GCD[2974:280429] 别点了,就会执行一次
    2018-04-26 13:53:42.523634+0800 GCD[2974:280429] 别点了,就会执行一次
    2018-04-26 13:53:42.685084+0800 GCD[2974:280429] 别点了,就会执行一次
    

    还有一些其他的用用法我还没掌握,欢迎各位大牛批评指正。

    Demo 地址:https://github.com/MYLILUYANG/GCD-

    更加详细总结: http://www.cocoachina.com/ios/20180313/22573.html

    相关文章

      网友评论

          本文标题:iOS - GCD 用法详解

          本文链接:https://www.haomeiwen.com/subject/iorplftx.html