runLoop

来源:互联网 发布:帝国cms系统目录 编辑:程序博客网 时间:2024/05/22 13:43

RunLoop

RunLoop就是运行循环,处理app中的各种事件(比如触摸事件,定时器事件,Selector事件)
一个线程对应一个RunLoop,主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建,RunLoop在第一次获取时创建,线程结束时销毁

RunLoop的获得

  • Foundation
    • [NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
    • [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
  • Core Foundation
    • CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
    • CFRunLoopGetMain(); // 获得主线程的RunLoop对象

RunLoop相关类

  • Core Foundation中关于RunLoop的5个类
    • CFRunLoopRef
    • CFRunLoopModeRef
    • CFRunLoopSourceRef
    • CFRunLoopTimerRef
    • CFRunLoopObserverRef

RunLoop的组成

  • Mode
    • Timer
    • source
    • Observer

Mode

Mode代表RunLoop的运行模式。每次RunLoop职能指定一个Mode运行。切换Mode职能退出Loop,再重新指定一个Mode进入。
一个 RunLoop 包含若干个 Mode,每个Mode又包含若干个Source/Timer/Observer

系统默认的5个Mode

  • NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行

  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

    • UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

    • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

    • NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode(既执行Default中的事件也执行track中的事件)

Timer

CFRunLoopTimerRef是基于时间的触发器

CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响

GCD的定时器不受RunLoop的Mode影响

- (void)timer{    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];    // 定时器只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作(比如textField的拖动)    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];    // 定时器只运行在UITrackingRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作    //    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];    // 定时器会跑在标记为common modes的模式下    // 标记为common modes的模式:UITrackingRunLoopMode和NSDefaultRunLoopMode    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];}- (void)timer2{    // 调用了scheduledTimer返回的定时器,已经自动被添加到当前runLoop中,而且是NSDefaultRunLoopMode    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];    // 修改模式    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];}- (void)run{    NSLog(@"----run");}

Source

  • CFRunLoopSourceRef是事件源(输入源)

  • 按照官方文档,Source的分类

    • Port-Based Sources (跟其他线程交互,或通过内核发送的消息)
    • Custom Input Sources (自动以输入源,几乎不用)
    • Cocoa Perform Selector Sources (比如performSelecter等方法)
  • 按照函数调用栈,Source的分类

    • Source0:非基于Port的
    • Source1:基于Port的,通过内核和其他线程通信,接收、分发系统事件

事件的传入都是先传入Source1。然后再由Source1分发至source0进行处理。所以在函数调用栈中往往只看到Source0。

Obersver

CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变

  • 可以监听的时间点
/* Run Loop Observer Activities */typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {    kCFRunLoopEntry = (1UL << 0), // 1    kCFRunLoopBeforeTimers = (1UL << 1), // 2    kCFRunLoopBeforeSources = (1UL << 2), // 4    kCFRunLoopBeforeWaiting = (1UL << 5), // 32    kCFRunLoopAfterWaiting = (1UL << 6), // 64    kCFRunLoopExit = (1UL << 7), // 128    kCFRunLoopAllActivities = 0x0FFFFFFFU};
  • 添加Observer
- (void)observer{    // 创建observer    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {        NSLog(@"----监听到RunLoop状态发生改变---%zd", activity);    });    // 添加观察者:监听RunLoop的状态    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);    // 释放Observer    //凡是带有Create、Copy、Retain等字眼的函数,创建出来的对象,都需要在最后做一次release    //比如CFRunLoopObserverCreate    //release函数:CFRelease(对象);    CFRelease(observer);}

RunLoop处理逻辑

  • 1:通知观察者run loop已经启动
  • 2:通知观察者任何即将要开始的定时器
  • 3:通知观察者任何即将启动的source0
  • 4:启动任何准备好的source0
  • 5:如果接收到source1准备好并处于等待状态,立即启动,并进入步骤9
  • 6:通知观察者线程进入休眠
  • 7:将线程置于休眠直到任何一下面的事件发生:
    • 某一事件到达source1
    • 定时器启动
    • runloop设置的时间超时
    • runloop被显式唤醒
  • 8:通知观察者线程将被唤醒
  • 9:处理未处理的事件
    • 如果用户定义的定时器启动,处理定时器事件并重启runloop,进入步骤2
    • 如果输入源启动,传递相应的消息
    • 如果runloop被显示唤醒而且时间还未超时,重启runloop,进入步骤2
  • 10:通知观察者runloop结束
0 0