延时执行
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 --");
}
结果:
- (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 --");
}
结果:
同步栅栏和异步栅栏的区分:
- 先执行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---");
结果:
// 对比普通的for循环遍历
for (int i=0; i<10; i++) {
NSLog(@"---%zd---%@",index,[NSThread currentThread]);
}
NSLog(@"---end---");
结果:
总结:
dispatch_apply在串行队列中按照顺序执行,完全没有意义。在并发队列中创建了N个任务,如果是在异步队列中则异步执行(由打印结果显示),但并非所有任务不开辟线程,也有在主线程中完成的。最后由输出的“done”字符串可以看出但done一定会输出在最后的位置,因为dispatch_apply函数会等待所有的处理结束
队列组
假如有个需求:
- 下载图片1
- 下载图片2
- 合成2张图片
- 显示在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 --- ");
});
});
}
结果:
- (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;
});
});
}
结果:
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);
}];
}