GCD的常用函数(四)

延时执行

1.使用NSObject方法

// 延时执行,2秒之后执行run方法,object是传递的参数
[self performSelector:@selector(run) withObject:nil afterDelay:2];

2.使用GCD

// 2.0秒之后执行block中的操作,dispatch_get_main_queue()在主线程中执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"run -- %@",[NSThread currentThread]);
});

3.使用NSTimer

// 2秒时候执行run方法,userInfo是传递的参数,repeats:是否重复执行
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
栅栏
- (void)barrier {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 1 --",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 2 --",[NSThread currentThread]);
    });
    
    /*
    * 虽然都是开启了子线程,但是dispatch_barrier_async会让在上面的代码先执行完,在执行下面的代码
    */
    dispatch_barrier_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- barrier --",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 3 --",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 4 --",[NSThread currentThread]);
    });
    NSLog(@"-- end --");
}

结果:


image
- (void)barrier {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 1 --",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 2 --",[NSThread currentThread]);
    });
    
    // 同步函数,会先执行
    dispatch_barrier_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- barrier --",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 3 --",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@ -- 4 --",[NSThread currentThread]);
    });
    NSLog(@"-- end --");
}

结果:


image

同步栅栏和异步栅栏的区分

  • 先执行barrier方法,前面的2个开辟了子线程中,第3个是异步执行开辟子线程,等执行完barrier方法后,在执行子线程中的dispatch_async,不过需要先等dispatch_barrier_async前面的dispatch_async先执行完在执行后面的dispatch_async
  • 先执行barrier方法,前面的2个开辟了子线程中,第3个是同步执行没有开辟线程,需要先执行dispatch_barrier_sync,执行完dispatch_barrier_sync后继续执行barrier,执行完barrier方法后,在执行子线程中的dispatch_async
一次性代码
// 在整个程序中,只运行一次
static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"run -- ");
});
/*
* 使用dispatch_once用来创建单例
*/
#import "WSimgleObject.h"

@implementation WSimgleObject

static WSimgleObject *obj = nil;
+ (instancetype)share {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        obj = [[WSimgleObject alloc]init];
    });
    return obj;
}

@end
快速迭代
// 使用dispatch_apply进行快速迭代遍历,只在并发队列中有效
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t index) {
    // 执行10次代码,index的顺序不一定
    NSLog(@"---%zd---%@",index,[NSThread currentThread]);
});
NSLog(@"---end---");

结果:


GCD的常用函数(四)_第1张图片
image
// 对比普通的for循环遍历
for (int i=0; i<10; i++) {
    NSLog(@"---%zd---%@",index,[NSThread currentThread]);
}
NSLog(@"---end---");

结果:


GCD的常用函数(四)_第2张图片
image

总结
dispatch_apply在串行队列中按照顺序执行,完全没有意义。在并发队列中创建了N个任务,如果是在异步队列中则异步执行(由打印结果显示),但并非所有任务不开辟线程,也有在主线程中完成的。最后由输出的“done”字符串可以看出但done一定会输出在最后的位置,因为dispatch_apply函数会等待所有的处理结束

队列组

假如有个需求:

  1. 下载图片1
  2. 下载图片2
  3. 合成2张图片
  4. 显示在UI界面上

因为下载图片、合成图片比较耗时,我们想把这些操作放到子线程中去操作,在图片下载完并合成的时候在回到主线程刷新UI界面。但是假如我们还是按照上面的开启子线程的方法,我们并不知道会先执行那个操作,有可能是先执行了合成图片的操作,这就出现了问题,所以就需要队列组

// 队列组
- (void)notify
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^{
        NSLog(@"-- 1 -- %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"-- 2 -- %@",[NSThread currentThread]);
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"dispatch_group_notify --- ");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"dispatch_get_main_queue --- ");
        });
    });
}

结果:


image
- (void)notify
{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^{
        NSLog(@"%@ -- 1",[NSThread currentThread]);
        // 下载图片1
        NSURL *url = [NSURL URLWithString:@"https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3113153671,3782273400&fm=27&gp=0.jpg"];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        self.image1 = [UIImage imageWithData:imageData];
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"%@ -- 2",[NSThread currentThread]);
        // 下载图片2
        NSURL *url = [NSURL URLWithString:@"https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=949354588,677085984&fm=27&gp=0.jpg"];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        self.image2 = [UIImage imageWithData:imageData];
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"%@ -- dispatch_group_notify",[NSThread currentThread]);
        // 拼接图片
        // 开启新的图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        // 绘图
        [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
        [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
        // 获取上下文的图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        // 关闭上下文
        UIGraphicsEndImageContext();
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"dispatch_get_main_queue --- ");
            self.imageView.image = image;
        });
    });
    
}

结果:


image

dispatch_group_enter(group)和dispatch_group_leave(group)

上面的队列组并不能解决所有的线程依赖问题,比如:当我在子线程中再开启子子线程请求图片的时候,这时候在子线程中的任务只会执行到发起请求,具体请求什么时候返回结果,子线程并不关心,这时候2个子线程都执行完图片请求就会执行dispatch_group_notify里面的操作,但是这时候图片可能还没有请求过来。这种情况可以使用dispatch_group_enter(group)和dispatch_group_leave(group)解决。

enter和leave应该配合使用,有几次enter就应该有几次leave,否则group会一直存在。当所有的enter都被leave的时候,就会执行dispatch_group_notify中的操作。所以我们在每次请求开始的时候enter,在请求结束(成功或失败)时leave,当所有的请求结束的时候就会执行dispatch_group_notify中的操作。

dispatch_group_t group = dispatch_group_create();
self.group = group;
dispatch_group_enter(self.group);
[self requestImageAPI1];
dispatch_group_enter(self.group);
[self requestImageAPI2];
dispatch_group_enter(self.group);
[self requestImageAPI3];
    
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
    NSLog(@"dispatch_group_notify --- 图片请求完之后的操作");
});

- (void)requestImageAPI1 {
    [[WAPIRequestManager share] requestDataWithMethod:GET URLString:URL_GetImage001 Params:nil Success:^(id  _Nullable data, NSDictionary * _Nullable status) {
        dispatch_group_leave(self.group);
    } Fail:^(NSString * _Nullable errorMsg, NSError * _Nullable error) {
        dispatch_group_leave(self.group);
    }];
}
- (void)requestImageAPI2 {
    [[WAPIRequestManager share] requestDataWithMethod:GET URLString:URL_GetImage002 Params:nil Success:^(id  _Nullable data, NSDictionary * _Nullable status) {
        dispatch_group_leave(self.group);
    } Fail:^(NSString * _Nullable errorMsg, NSError * _Nullable error) {
        dispatch_group_leave(self.group);
    }];
}
- (void)requestImageAPI3 {
    [[WAPIRequestManager share] requestDataWithMethod:GET URLString:URL_GetImage003 Params:nil Success:^(id  _Nullable data, NSDictionary * _Nullable status) {
        dispatch_group_leave(self.group);
    } Fail:^(NSString * _Nullable errorMsg, NSError * _Nullable error) {
        dispatch_group_leave(self.group);
    }];
}

你可能感兴趣的:(GCD的常用函数(四))