简介

上一篇已经介绍了iOS中的多线程基本原理和NSThread的基本使用,接下来我们简单介绍第二种开启线程的方式GCD,在了解GCD之前我们首先了解同步函数、异步函数、串行队列、并发队列

基本概念

GCD:它的全称是Grand Central Dispatch ,强大的中枢调度器。
同步函数:同步指的是只能在当前线程中执行任务,不具备开启新线程的能力。
异步函数:异步指的是可以在新的线程中执行任务,具备开始新线程的能力。
并发队列:并发队列指的是可以让多个任务并发(同时)执行,自动开启多个线程同时执行任务。
串行队列:串行队列指的是让任务一个接一个的执行,一个任务执行完毕才继续执行下一个。只要是带串的你们就可以联想成糖葫芦。

任务和队列结合,对应的组合特点
  • 同步函数 + 主队列 (不用解释了肯定在主线程中执行)
// 1.获得主队列
     dispatch_queue_t queue = dispatch_get_main_queue();
     // 2.将任务加入队列
     dispatch_sync(queue, ^{
         NSLog(@"1-----%@", [NSThread currentThread]);
     });
     dispatch_sync(queue, ^{
         NSLog(@"2-----%@", [NSThread currentThread]);
     });
     dispatch_sync(queue, ^{
         NSLog(@"3-----%@", [NSThread currentThread]);
     });
  • 异步函数 + 主队列:只在主线程中执行任务
// 1.获得主队列
 dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_async(queue, ^{
    NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"3-----%@", [NSThread currentThread]);
});
  • 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
// 1.创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("ai.bobo.queue", DISPATCH_QUEUE_SERIAL);
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
  • 同步函数 + 并发队列:不会开启新的线程
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 2.将任务加入队列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
  • 异步函数 + 并发队列:可以同时开启多条线程
// 1.获得全局的并发队列
 	    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 	    
 	    // 2.将任务加入队列
 	    dispatch_async(queue, ^{
 	        for (NSInteger i = 0; i<10; i++) {
 	            NSLog(@"1-----%@", [NSThread currentThread]);
 	        }
 	    });
 	    dispatch_async(queue, ^{
 	        for (NSInteger i = 0; i<10; i++) {
 	            NSLog(@"2-----%@", [NSThread currentThread]);
 	        }
 	    });
 	    dispatch_async(queue, ^{
 	        for (NSInteger i = 0; i<10; i++) {
 	            NSLog(@"3-----%@", [NSThread currentThread]);
 	        }
 	    });
GCD线程中的通信

我们在开发中使用GCD 开启一条子线程执行耗时操作,然后子线程任务执行完之后获取到对应的数据,我们将数据展示到手机页面上,这样涉及到操作UI页面,那么必须回到主线程中进行操作。 其实也很简单,只需要将队列设置dispatch_get_main_queue() 就可以了,代码如下,模拟一个加载网络图片的操作。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           // 图片的网络路径
           NSURL *url = [NSURL URLWithString:@"https://i1.hdslb.com/bfs/face/5cb955002eab27132543f9d8c86e8f3beac2ab4c.jpg"];
           // 加载图片
           NSData *data = [NSData dataWithContentsOfURL:url];
           // 生成图片
           UIImage *image = [UIImage imageWithData:data];
           // 回到主线程
           dispatch_async(dispatch_get_main_queue(), ^{
               self.imageView.image = image;
           });
       });
   }
dispatch_barrier_async 栅栏的使用

在开发中我可能开启多个线程,但是有时候我在其中的一个线程中要用到其中一个线程中的数据,无法确认他们执行的顺序,苹果提供了dispatch_barrier_async这个函数,栅栏就是阻挡的意思,比较形象,就是在栅栏前的先执行,前面执行完了之后在执行栅栏函数和之后的任务。例如异步函数中有三条线程,线程3必须在线程1、2执行完成之后才执行,我们就可以使用栅栏挡一下。代码如下

- (void)barrier
	{
	    dispatch_queue_t queue = dispatch_queue_create("aibobo", DISPATCH_QUEUE_CONCURRENT);
	    
	    dispatch_async(queue, ^{
	        NSLog(@"----1-----%@", [NSThread currentThread]);
	    });
	    dispatch_async(queue, ^{
	        NSLog(@"----2-----%@", [NSThread currentThread]);
	    });
	    //设置栅栏,让前面先执行
	    dispatch_barrier_async(queue, ^{
	        NSLog(@"----barrier-----%@", [NSThread currentThread]);
	    });
	    dispatch_async(queue, ^{
	        NSLog(@"----3-----%@", [NSThread currentThread]);
	    });
	}
GCD中的延迟执行与执行一次的函数
- (void)delay
  {   
      //dispatch_after方法 2.0 * NSEC_PER_SEC代表2秒之后执行
          dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
              NSLog(@"run-----");
          });
     
    //还有其他方法实现:除了前面NSThread的睡的方法
    //2秒之后在执行
          [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
         //定时器也可以实现
      [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
  }
  //dispatch_once只执行一次,开发者常用于单例模式的设计中
  - (void)once
  {
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
          NSLog(@"------run");
      });
}
dispatch_group_async线程组,这个在开发中用的也比较多,用法的场景和dispatch_barrier_async一样,下面举例一个合成图片
- (void)group
	{
	    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
	    // 创建一个队列组
	    dispatch_group_t group = dispatch_group_create();
	    // 1.下载图片1
	    dispatch_group_async(group, queue, ^{
	        // 图片的网络路径
	        NSURL *url = [NSURL URLWithString:@"网络图片1"];
	        // 加载图片
	        NSData *data = [NSData dataWithContentsOfURL:url];
	        
	        // 生成图片
	        self.image1 = [UIImage imageWithData:data];
	    });
	    
	    // 2.下载图片2
	    dispatch_group_async(group, queue, ^{
	        // 图片的网络路径
	        NSURL *url = [NSURL URLWithString:@"网络图片2"];
	        // 加载图片
	        NSData *data = [NSData dataWithContentsOfURL:url];
	        // 生成图片
	        self.image2 = [UIImage imageWithData:data];
	    });
	    // 3.将图片1、图片2合成一张新的图片
	    dispatch_group_notify(group, queue, ^{
	        // 开启新的图形上下文
	        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
	        // 绘制图片
	        [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
	        [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
	        // 取得上下文中的图片
	        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
	        // 结束上下文
	        UIGraphicsEndImageContext();
	        // 回到主线程显示图片
	        dispatch_async(dispatch_get_main_queue(), ^{
	            // 4.将新图片显示出来 
	            self.imageView.image = image;
	        });
	    });
	}