iOS开发之技能点iOS学习笔记iOS 开发

教你如何轻松搞定 Runloop

2016-10-09  本文已影响492人  BWLi420

认识 Runloop

Runloop 的本质

Runloop 的作用

Runloop 与线程的关系

获取 Runloop 对象

NSRunLoop *mainRL = [NSRunLoop mainRunLoop];
CFRunLoopRef mainRLRef = CFRunLoopGetMain();
```

NSRunLoop *currentRL = [NSRunLoop currentRunLoop];
CFRunLoopRef currentRlRef = CFRunLoopGetCurrent();
```

NSRunLoop *curRunloop = [NSRunLoop currentRunLoop];
```
这个方法本身是懒加载的,如果是第一次调用该方法,那么就创建子线程对应的 Runloop。

Runloop 的相关类

与 Runloop 相关的共有五个类:CFRunLoopRef、CFRunLoopModeRef、CFRunLoopTimerRef、CFRunLoopSourceRef、CFRunLoopObserverRef


Runloop 五个相关类之间的关系
  1. CFRunLoopRef( Runloop 对象)

    • Runloop 对象就是 Runloop 本身
  2. CFRunLoopModeRef( Runloop 的运行模式)

    • 一个 Runloop 包含若干个 Mode,而每个 Mode 又包含若干个 Source/Timer/Observer,每次 RunLoop 启动时,只能指定其中一个 Mode,这个 Mode 被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入,这样就可以分隔开不同组的 Source/Timer/Observer,让其互不影响。
    • Mode 的分类,系统默认注册了 5 个 Mode:
      • kCFRunLoopDefaultMode:App 的默认 Mode,通常主线程是在这个 Mode 下运行
      • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
      • UIInitializationRunLoopMode: 在刚启动 App 时进入的第一个 Mode,启动完成后就不再使用
      • GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
      • kCFRunLoopCommonModes: 占位用的 Mode,不是一种真正的 Mode,就相当于 KCFRunLoopDefaultMode 和 UITrackingRunLoopMode的合体
  3. CFRunLoopTimerRef( Timer 事件)

    • 基于时间的触发器,基本等同于 NSTimer

    • NSTimer 在各种模式下的运行效果

    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    ```
    以上 scheduledTimerWithTimeInterval 方法内部默认把创建的定时器对象添加到当前的 Runloop 中,并且指定运行模式为 NSDefaultRunLoopMode

     ```
    

    NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    ```
    以上 timerWithTimeInterval 方法创建定时器,如果想要定时器工作,还需要添加到 Runloop 中,并指定运行模式

    • 注意:当 Runloop 切换到非指定模式,定时器就会停止工作
  4. CFRunLoopSourceRef( Runloop 要处理的事件源)

    • 事件源也就是输入源,只需要对它的分类有所了解就可以了
    • 以前的分法(根据官方文档)
      • Port-Based Sources(基于端口的源)
      • Custom Input Sources(自定义输入源)
      • Cocoa Perform Selector Sources(可执行选择器源)
    • 现在的分法(基于函数的调用栈)
      • Source0:非基于 Port 的(用户操作事件)
      • Source1:基于 Port 的(系统事件)
  5. CFRunLoopObserverRef( Runloop 的监听者)

    • 主要用于监听 Runloop 的状态

    • Runloop 的状态主要有:

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0), //即将进入 Runloop
    kCFRunLoopBeforeTimers = (1UL << 1), //即将处理 NSTimer
    kCFRunLoopBeforeSources = (1UL << 2), //即将处理 Sources
    kCFRunLoopBeforeWaiting = (1UL << 5), //即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6), //刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7), //即将退出 Runloop
    kCFRunLoopAllActivities = 0x0FFFFFFFU //所有状态改变
    };
    ```

    • 实现 Runloop 的监听

    //创建监听对象,当 Runloop 的状态改变时就会调用该方法
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    switch (activity) {
    case kCFRunLoopEntry:
    NSLog(@"进入runloop");
    break;
    case kCFRunLoopBeforeTimers:
    NSLog(@"即将处理time事件");
    break;
    case kCFRunLoopBeforeSources:
    NSLog(@"即将处理source事件");
    break;
    case kCFRunLoopBeforeWaiting:
    NSLog(@"即将休眠");
    break;
    case kCFRunLoopAfterWaiting:
    NSLog(@"runloop被唤醒");
    break;
    case kCFRunLoopExit:
    NSLog(@"runloop退出");
    break;

         default:
             break;
     }
    

});
//设置监听
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
```

Runloop 的启动

  1. 选择一个运行模式,且只能选择一个
  2. 判断当前选择的运行模式是否为空
  3. 检查当前运行模式里面是否有 Source 或 Timer,如果都没有,则 Runloop 立即退出
  4. 至少有 Source 或 Timer 中的任意一个,则 Runloop 开启
  5. 在检查的时候不会检查 Observer

Runloop 的运行处理逻辑

Runloop 的运行处理逻辑

Runloop 的应用

void msg(int n)
{
NSLog(@"runloop被唤醒");
NSLog(@"runloop处理事件---%zd",n);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"runloop启动了");

        do {
            NSLog(@"runloop询问,还有事情需要我处理吗?");
            NSLog(@"没有事情的话,我就睡觉了");
            NSLog(@"runloop进入到休眠");
      
            int number = 0;
            scanf("%zd",&number);
            msg(number);
        
        } while (1);
    }
    return 0;

}
```

上一篇下一篇

猜你喜欢

热点阅读