美文网首页
RunLoop 初识

RunLoop 初识

作者: 游城十代2dai | 来源:发表于2020-04-13 20:39 被阅读0次

0x00 RunLoop 是什么?

在程序运行的过程中循环做的一些事情, 保证 main 函数不会直接退出, 并处理各种事件, 节省 CPU 资源, 提高性能

0x01 应用在哪里?

  • 定时器, PerformSelector
    • 解决 NSTimer 在滑动时停止工作的问题
    NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
          // handle
      }];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    
  • GCD, 线程
    • 控制线程的生命周期(线程保活)
    // 创建 thread  并启用 RunLoop  
    - (void)run {
      NSLog(@"%s  ---  START", __func__);
      [NSRunLoop.currentRunLoop addPort:NSPort.new forMode:NSDefaultRunLoopMode];
    //    run 是一个不会停止的循环, 永不销毁的RunLoop
    //    [NSRunLoop.currentRunLoop run];
      while (self && !self.needThreadStop) {
          [NSRunLoop.currentRunLoop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture];
      }
      
      NSLog(@"%s --- END", __func__);
    }
    // 停止 RunLoop, 并挂掉线程
    - (void)stop {
      NSLog(@"%s --- %@", __func__, NSThread.currentThread);
      self.needThreadStop = YES;
      CFRunLoopStop(CFRunLoopGetCurrent());
      self.thread = nil;
      NSLog(@"%s --- %@", __func__, NSThread.currentThread);
    }
    // 点击方法, 触发 stop
    - (IBAction)threadStop:(id)sender {
      [self performSelector:@selector(stop) onThread:self.thread withObject:nil waitUntilDone:YES];
    }
    
  • 事件的响应, 手势的识别, UI 的刷新
    • 监控应用卡顿
    • 性能优化
  • 网络请求
  • AutoreleasePool

0x02 RunLoop 对象

有两套 API 可以获取

  • NSRunLoop : 基于 CFRunLoopRef 进行包装的 OC 对象, [NSRunLoop currentRunLoop]
  • CFRunLoopRef: 是一套开源的代码 CFRunLoopGetCurrent()

0x03 线程

  • 每条线程都有唯一的一个与之对应的 RunLoop 对象(一一对应关系)
  • RunLoop 保存在一个全局的 Dic 里面, 线程作为 key, RunLoop 作为 value
  • 线程刚创建的时候并没有 RunLoop 对象, RunLoop 对象在第一次获取时创建, 即 [NSRunLoop currentRunLoop]CFRunLoopGetCurrent()
  • RunLoop 在线程结束时销毁
  • 主线程的 RunLoop 是自动获取的, 子线程默认没有开启 RunLoop, 需要手动开启, 比如 dispatch_async, 使用的时候并没有 RunLoop, 需要在 block 内获取一次才会创建

0x04 RunLoop 的类

  • 息息相关的五个类

    • typedef struct __CFRunLoop * CFRunLoopRef;

    • typedef struct __CFRunLoopMode *CFRunLoopModeRef;

    • typedef struct __CFRunLoopSource * CFRunLoopSourceRef;

    • typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;

    • typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;

  • RunLoop 最关心的结构体(简化版本, 原版本去看源码吧):

struct __CFRunLoop {
    pthread_t _pthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
};


struct __CFRunLoopMode {
    CFStringRef _name;
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
};

0x05 CFRunLoopModeRef

  • CFRunLoopModeRef 代表 RunLoop 的运行模式
  • 一个 RunLoop 有多个模式(结构体中 _modes 体现), 每个模式又包含若干个Source0/Source1/Observer/Timer (__CFRunLoopMode 结构体成员体现)
  • RunLoop 启动时只能选择一种模式, 作为 CurrentMode
  • 如果需要切换 Mode, 只有退出当前 RunLoop, 再选择一个 Mode 进入
  • 如果 RunLoop 中没有任何 Source0/Source1/Observer/Timer , 就会立即退出
  • 常见的 3 种模式:
    • NSDefaultRunLoopMode (kCFRunLoopDefaultMode) 默认的 Mode
    • UITrackingRunLoopMode 保证滑动时不受其他 Mode 影响
    • NSRunLoopCommonModes (kCFRunLoopCommonModes) 包含前两种, 并不是一种模式, 而是一个标志, 让系统去选择 CFMutableSetRef _commonModes;

0x06 CFRunLoopObserverRef

// 创建 
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { });
// 添加监听        
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
// 释放
CFRelease(observer);
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),   // 即将进入 Loop
    kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入睡眠
    kCFRunLoopAfterWaiting = (1UL << 6), // 刚从睡眠中唤醒
    kCFRunLoopExit = (1UL << 7), // 即将退出 Loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

0x07 运行逻辑

  • Sources0
    • 触摸事件
    • performSelector:onThread:
  • Sources1
    • 基于 Port 的线程通信
    • 系统事件捕捉(捕捉到后给 Source0 处理) (响应用户操作的流程)
  • Timers
    • NSTimer
    • performSelector: withObject: afterDelay:
  • Observers
    • 用于监听 RunLoop 的状态
    • UI 刷新(before waiting)
    • AutoreleasePool(before waiting)
  • 逻辑流程
    1. 通知 Observers : 进入 Loop (__CFRunLoopDoObservers 所有的通知操作)
    2. 通知 Observers : 即将处理Timers
    3. 通知 Observers : 即将处理Sources
    4. 处理 Blocks (__CFRunLoopDoBlocks)
    5. 处理Source0 (__CFRunLoopDoSources0) ( 函数返回值为 True 再次处理 Blocks )
    6. 如果存在 Source1 就跳转到第 8 步, 否则继续
    7. 通知 Observers : 开始睡眠( 等待唤醒 )
    8. 通知 Observers : 结束睡眠( 被唤醒 )
      • 处理 Timer (__CFRunLoopDoTimers)
      • 处理 GCD (CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE)
      • 处理 Source1 (__CFRunLoopDoSource1)
    9. 处理 Blocks
    10. 根据结果决定如何操作
      • 回到第二步继续处理事件
      • 退出 Loop (换 Mode)
    11. 通知 Observers : 退出 Loop

0x08 睡眠原理

  • 等待消息 (mach_msg 内核 API)
    • 没有消息就睡眠休息
    • 有消息就唤醒处理

相关文章

  • 初识RunLoop

    初识RunLoop 1.RunLoop的作用A 保持程序的持续运行(ios程序为什么能一直活着不会死)B 处理Ap...

  • Runloop初识

    一、作用: 1.保证程序持续运行 处理APP中的各种事件(如触摸事件、定时器事件、Selector事件) 节约cu...

  • 初识 RunLoop

    字面理解:运行循环,相当于在跑圈(400米) 基本作用: 1.保持程序的持续运行(就像我们运行一个程序,就会开启一...

  • RunLoop 初识

    0x00 RunLoop 是什么? 在程序运行的过程中循环做的一些事情, 保证 main 函数不会直接退出, 并处...

  • 初识runloop

    一、什么是runloop 没错runloop就是做这件事的。在程序的主入口 main 函数中将我们的代码包裹再wh...

  • 深入浅出 RunLoop(五):RunLoop 与 NSTime

    RunLoop 系列文章 深入浅出 RunLoop(一):初识深入浅出 RunLoop(二):数据结构深入浅出 R...

  • 深入浅出 RunLoop(四):RunLoop 与线程

    RunLoop 系列文章 深入浅出 RunLoop(一):初识深入浅出 RunLoop(二):数据结构深入浅出 R...

  • 深入浅出 RunLoop(三):事件循环机制

    RunLoop 系列文章 深入浅出 RunLoop(一):初识深入浅出 RunLoop(二):数据结构深入浅出 R...

  • 深入浅出 RunLoop(一):初识

    RunLoop 系列文章 深入浅出 RunLoop(一):初识深入浅出 RunLoop(二):数据结构深入浅出 R...

  • 深入浅出 RunLoop(二):数据结构

    RunLoop 系列文章 深入浅出 RunLoop(一):初识深入浅出 RunLoop(二):数据结构深入浅出 R...

网友评论

      本文标题:RunLoop 初识

      本文链接:https://www.haomeiwen.com/subject/ajawmhtx.html