GCD大家经常在开发中使用到,这个已经很熟悉了,而且异步的意义大家也都知道,今天要说的就是和异步相关的一个小知识,不讲那些老生常谈的东西。可能大家经常这样写,或许也能简单的说出一些门道,但是能完全搞清楚其中的意义吗?PS:真实经历,思考这个问题的时候,问了一个开发5年的前辈,他也不能清楚的说出其中的意义。最终还是自己探索清楚的,每当彻底搞清楚这样的小知识的时候,就是最开心最踏实的时候。
直接上代码
dispatch_async(dispatch_get_main_queue(), ^{
//主线程的一些操作
});
这段代码大家想必很熟悉,今天要说的就是这个问题。或许大家经常用到这个方法,最熟悉的场景莫过于在一个异步线程的block里面,耗时的操作完成后,下面紧接着写上这一段,也就是下面的这种情况
/**
* 异步线程(再开一条线程(子线程)执行下面的耗时操作)
*/
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//耗时操作
//************
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程,刷新UI
});
});
但是很多时候经常在项目中看到在主线程就直接撸上了第一段的代码,突然有一天,仔细一想,我X,这尼玛不对啊,首先主线程是个串行队列,在一个串行队列里面来个异步操作有什么意义呢?不还是一个一个的串着执行?模糊的解释就是说这个block里面的操作会延迟执行,”过一会“再执行。请教一通前辈之后,得到的也是这个模糊的答案
![惊讶](http://static.blog.csdn.net/xheditor/xheditor_emot/default/ohmy.gif)
其实根据经验也可以知道,这个时机肯定和runloop的跑圈息息相关,因为这些代码的执行就是在跑圈的过程中得以执行的。自然想到,监听runloop的跑圈,直接上代码
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self addObserver];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self testGCD];
});
}
- (void)testGCD
{
NSLog(@"test1输出");
/**
* 异步线程(再开一条线程(子线程)执行下面的耗时操作)
*/
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"test2输出");
});
NSLog(@"test3输出");
}
-(void)addObserver
{
//1.创建监听者
/*
第一个参数:怎么分配存储空间
第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态
第三个参数:时候持续监听
第四个参数:优先级 总是传0
第五个参数:当状态改变时候的回调
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
/*
kCFRunLoopEntry = (1UL << 0), 即将进入runloop
kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
kCFRunLoopBeforeSources = (1UL << 2),即将处理source事件
kCFRunLoopBeforeWaiting = (1UL << 5),即将进入睡眠
kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
kCFRunLoopExit = (1UL << 7), runloop退出
kCFRunLoopAllActivities = 0x0FFFFFFFU
*/
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即将进入runloop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timer事件");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理source事件");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入睡眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"被唤醒");
break;
case kCFRunLoopExit:
NSLog(@"runloop退出");
break;
default:
break;
}
});
/*
第一个参数:要监听哪个runloop
第二个参数:观察者
第三个参数:运行模式
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);
//NSDefaultRunLoopMode == kCFRunLoopDefaultMode
//NSRunLoopCommonModes == kCFRunLoopCommonModes
}
@end
从打印输出可以很清晰的看到,显然这个执行的间隔是在两趟跑圈里面,也就是说间隔了一个runloop循环的时间。(1/60秒)。
终于彻底搞清楚了,以后使用起来也更加的自信和清晰了。其实这个延迟很重要的,有些时候你在当前的这个循环里面没有拿到一些设置的属性(比如delegate,这也是项目实战的解Bug的症结所在,项目中的一个图片请求的控件的代理设置时间问题出现的Bug),可以在下一个循环时再做一些东西,自然而然的可以考虑这样做了。
同时附上另外一种与runloop循环相关的延迟操作的方案,如下图:
明显可以看出,也是间隔了一个循环的时间,这个小知识也可以在合适的场景下运用起来!