RunLoop的唤醒探索

概况

屏幕触摸事件,定时器事件是常见的可以唤醒runLoop的事件。但是我们很少去关注定时器是怎么唤醒额runLoop,唤醒后又进行了哪些操作。我们也很少关注我们触摸屏幕如点击一个按钮,是如何唤醒的runLoopo,唤醒多经历了哪些过程,点击完按钮又执行了什么。下边让我们来逐一探索。

定时器事件唤醒runLoop

  1. 首先我们先写个类去监听runLoop的状态
@implementation MainRunLoopObsever
+ (void)load{
    CFRunLoopObserverRef obseverRef = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSString *str = nil;
        switch (activity) {
            case kCFRunLoopEntry:
                str = @"kCFRunLoopEntry";
                break;
            case kCFRunLoopBeforeTimers:
                str = @"kCFRunLoopBeforeTimers";
                break;
            case kCFRunLoopBeforeSources:
                str = @"kCFRunLoopBeforeSources";
                break;
            case kCFRunLoopBeforeWaiting:
                str = @"kCFRunLoopBeforeWaiting";
                break;
            case kCFRunLoopExit:
                str = @"kCFRunLoopExit";
                break;
            case kCFRunLoopAllActivities:
                str = @"kCFRunLoopAllActivities";
                break;
            case kCFRunLoopAfterWaiting:
                str = @"kCFRunLoopAfterWaiting";
                break;
                
            default:
                break;
        }
        if (str == nil) {
            NSLog(@"%ld",activity);
        }else{
            NSLog(@"%@",str);
        }
    });
    
    CFRunLoopAddObserver(CFRunLoopGetMain(), obseverRef, kCFRunLoopCommonModes);
}
@end

  1. 之后再写定时器代码
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [NSTimer scheduledTimerWithTimeInterval:10.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"timer事件");
    }];
}

@end
  1. 观察控制台的打印
    在NSLog部分打个断点,程序运行起来,观察控制台的打印。发现在控制台输入kCFRunLoopAfterWaiting之后进入了断点执行了我们写的block,之后我们在解除断点跑起来程序发现之后依次输出timer事件,kCFRunLoopBeforeTimers,kCFRunLoopBeforeSources,kCFRunLoopBeforeWaiting。
    说明定时器事件唤醒了runLoop之后执行了kCFRunLoopAfterWaiting这个obsever。之后执行我们的定时任务输出timer事件,在执行完毕后执行了kCFRunLoopBeforeTimers(obsever),再之后执行了kCFRunLoopBeforeSources(obsever),最后执行了kCFRunLoopBeforeWaiting(obsever)。之后其实要倾倒释放池,重新创建新的释放池,并更新UI,最后CPU进入睡眠态。

屏幕事件唤醒runLoop

  1. 屏幕事件唤醒runLoop稍复杂些。唤醒前要经历一些系统的处理
  2. 当我们点击一个按钮后系统的IOKit.framework会生成一个IOHIDEvent事件。这个事件先经过springBoard进行过滤(目的是看是否是摇晃事件,音量事件等)
  3. 过滤后确认是屏幕事件。这时候会通过matchPoart触发source1事件,source1事件唤醒了runLoop。
  4. 之后执行kCFRunLoopAfterWaiting这个obsever。然后去处理source1,之后执行kCFRunLoopBeforeTimers(obsever),再之后执行了kCFRunLoopBeforeSources(obsever),之后执行了source0事件
  5. 该事件内部把IOHIDEvent事件封装成了UIEvent事件。并触发了响应链规则(即先通过hitTest,pointInside两个方法找到点击的那个view,之后通过响应链来找到要执行的方法。然后去执行这个方法)执行完source0后,会继续检查是否有source1事件了如果有就去执行,再一次循环(这样做的目的在于省去了cpu睡眠时间,任务多就去直接执行即可)。
  6. 之后执行了kCFRunLoopBeforeWaiting,倾倒释放池,创建新的释放池,并更新UI,这时候我们就看到了界面的UI变化了。这也解释了,为什么只有与当程序完全跑起来了,cpu进入睡眠了才可以看到UI界面的变化。
  7. 由于以上执行过程较为复杂,所以就不一一验证了。另外hiteTest,pointInside两个方法也很巧妙的可以确定当前view。也是值得我们研究的。这里也不一一解释了,有兴趣的去找度娘吧,大把大把的解释。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值