首页 > 技术文章 > iOS多线程之6.GCD的其他用法

doujiangyoutiao 2016-11-05 10:35 原文

队列组

让队列里的任务同时执行,当任务都执行完毕时,再以通知的形式告诉程序员。举例,同时下载两张图片,两张图片都下载完了,在合成成一张。
代码:

#import "ViewController.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIImageView *imageView1;
@property (weak, nonatomic) IBOutlet UIImageView *imageView2;
@property (weak, nonatomic) IBOutlet UIImageView *imageView3;
@end

@implementation ViewController
// 点击屏幕开始下载图片
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    // 创建全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 创建队列组
    dispatch_group_t group = dispatch_group_create();
    
    __block UIImage *image1 = nil;
    // 开启一个任务
    dispatch_group_async(group, queue, ^{
        NSLog(@"%@开始下载第一张图片",[NSThread currentThread]);
        NSString *strURL1 = @"http://h.hiphotos.baidu.com/zhidao/pic/item/6d81800a19d8bc3ed69473cb848ba61ea8d34516.jpg";
        image1 = [self downloadImageWithURL:strURL1];
    });
    
    // 开启一个任务
    __block UIImage *image2 = nil;
    dispatch_group_async(group, queue, ^{
        NSLog(@"%@开始下载第二张图片",[NSThread currentThread]);
        NSString *strURL2 = @"http://h.hiphotos.baidu.com/zhidao/pic/item/0eb30f2442a7d9334f268ca9a84bd11372f00159.jpg";
        image2 = [self downloadImageWithURL:strURL2];
    });
    
    // 同时执行下载图片1\下载图片2的操作
    
    // 等group里的任务执行完毕,执行的操作
    // 回到主线程显示图片
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"%@显示图片",[NSThread currentThread]);
        self.imageView1.image = image1;
        self.imageView2.image = image2;
        // 合并两张图片图片
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 50), NO, 0.0);
        [image1 drawInRect:CGRectMake(0, 0, 50, 50)];
        [image2 drawInRect:CGRectMake(50, 0, 50, 50)];
        self.imageView3.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    });
}
- (UIImage *)downloadImageWithURL : (NSString *)strURL {
    NSURL *url = [NSURL URLWithString:strURL];
    return [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
}
@end

日志

2016-11-05 09:03:49.300 TTTTTTTTTT[2543:26927] <NSThread: 0x7ba80500>{number = 3, name = (null)}开始下载第一张图片
2016-11-05 09:03:49.301 TTTTTTTTTT[2543:28894] <NSThread: 0x7ba80b10>{number = 4, name = (null)}开始下载第二张图片
2016-11-05 09:03:49.453 TTTTTTTTTT[2543:26806] <NSThread: 0x79773680>{number = 1, name = main}显示图片

效果:

如果不用队列组,下载第一张图片、下载第二张图片、合并两张图片,就只能当做一个任务放入队列中,不能同时下载两张图片,耗时几乎多了一倍。因为,如果你不这样做(当做一个任务放入队列中),你不知道两张图片什么时候下载完,谁先下载完,因为每次都不确定,不知道什么时候合并图片。现在用group,由系统来帮我们监听两张图片下载的进度,咱们只要把两张图片都下载完的程序写写进dispatch_group_notify就好了。

延迟执行

延迟执行就是让程序过一会在执行某一段代码。用两种方法:

- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
dispatch_after(dispatch_time_t when,
   dispatch_queue_t queue,
   dispatch_block_t block);

代码

- (void)viewDidLoad {
    [super viewDidLoad];
    // 第一种
    [self performSelector:@selector(run) withObject:nil afterDelay:2];
    // 第二种 可以安排执行的队列 3秒后执行
    // 主队列
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"当前线程2%@",[NSThread currentThread]);
        NSLog(@"GCD主队列过一会执行我");
    });
    // 全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
        NSLog(@"当前线程3%@",[NSThread currentThread]);
        NSLog(@"GCD全局并发队列过一会执行我");
    });
}
- (void)run {
    NSLog(@"当前线程1%@",[NSThread currentThread]);
    NSLog(@"过一会执行我");
}

日志

2016-11-05 09:27:04.903 TTTTTTTTTT[3439:39246] 当前线程1<NSThread: 0x7b6629a0>{number = 1, name = main}
2016-11-05 09:27:04.903 TTTTTTTTTT[3439:39246] 过一会执行我
2016-11-05 09:27:06.176 TTTTTTTTTT[3439:39246] 当前线程2<NSThread: 0x7b6629a0>{number = 1, name = main}
2016-11-05 09:27:06.176 TTTTTTTTTT[3439:39291] 当前线程3<NSThread: 0x7d175a90>{number = 3, name = (null)}
2016-11-05 09:27:06.177 TTTTTTTTTT[3439:39246] GCD主队列过一会执行我
2016-11-05 09:27:06.177 TTTTTTTTTT[3439:39291] GCD全局并发队列过一会执行我

  延迟执行我们常用的可能是第一种方法。从那个线程中调用“ performSelector”,run方法就在哪个线程中执行可,一般是主线程。第二种方法是可以自定义方法执行的队列,可以是主队列,也可以是全局队列。本人比较喜欢用block,所以喜欢第二种,因为都写在一起,增加了代码的可读性。

一次性代码

  一次性代码主要是在单例中应用。

#import "Person.h"

@implementation Person

static Person *person;
// 1
- (instancetype)shareInstance1 {
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        person = [[Person alloc] init];
    });
    return person;
}
// 2.
- (instancetype)shareInstace2 {
    
    if (!person) {
        person = [[Person alloc] init];
    }
    return person;
}
@end

  dispatch_once里的代码在整个程序运行过程中就执行一次!!!所以你有这方面的需求,也可以用这个。
  其实关于GCD,今天是最后一篇文章了,其实GCD还有很多地方我没有讲到,一方面是我水平有限,一方面是不常用。从下一篇开始,我就开始讲NSOperation了。例外,如果大家关于GCD还有什么想知道的,可以留言,我知道的一定告诉。

推荐阅读