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---");

结果:


image
// 对比普通的for循环遍历
for (int i=0; i<10; i++) {
    NSLog(@"---%zd---%@",index,[NSThread currentThread]);
}
NSLog(@"---end---");

结果:


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);
    }];
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容