RunLoop

作者: iVikings | 来源:发表于2020-05-09 10:35 被阅读0次

RunLoop 顾名思义:运行循环,在程序运行过程中循环做一些事情,即用来处理事件的循环

RunLoop 应用范畴

  • 定时器(Timer)、PerformSelector
  • GCD Async Main Queue
  • 事件响应、手势识别、界面刷新
  • 网络请求(如:AFNetworking 线程保活)
  • AutoreleasePool

RunLoop 基本作用

  • 保持程序持续运行:程序一启动就会创建一个主线程,一个主线程会有与之对应的RunLoopRunLoop保证主线程不会被销毁,也就保证了程序的持续运行
  • 处理App中的各种事件(比如:触摸事件、定时器事件、Selector事件等)
  • 节省 CPU 资源,提高程序性能:程序运行起来时,当什么操作都没有做的时候,RunLoop就会通知 CPU,现在没有事情做,我要去休息,这时CPU 就会将其资源释放出来去做其他的事情,当有事情做的时候RunLoop就会被唤醒去做事情。(没有消息就让线程休眠,有消息就唤醒线程)

RunLoop 对象

iOS 中有2套API来访问和使用 RunLoop:

  • Foundation:NSRunLoop
  • Core Foundation:CFRunLoopRef

NSRunLoopCFRunLoopRef 都代表着 RunLoop对象

RunLoop 与线程

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop 保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
  • 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

获取 RunLoop 对象

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

RunLoop 的构成

RunLoop
  • Core Foundation 中关于 RunLoop 的5个类

    CFRunLoopRef // runloop对象
      CFRunLoopModeRef  // 运行模式
        CFRunLoopSourceRef
        CFRunLoopTimerRef
        CFRunLoopObserverRef
    

结构体如下:

typedef struct __CFRunLoop * CFRunLoopRef;
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;
typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;

struct __CFRunLoop {
    pthread_t _pthread; // 线程 
    CFMutableSetRef _commonModes; // commonModes下的两个mode(kCFRunloopDefaultMode和UITrackingMode)
    CFMutableSetRef _commonModeItems; // 在commonModes状态下运行的对象(例如Timer)
    CFRunLoopModeRef _currentMode; // 运行的所有模式(CFRunloopModeRef类)
    CFMutableSetRef _modes; // 在当前 loop 下运行的mode
    ...
};

typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
    CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    ...
};

CFRunLoopModeRef

  • CFRunLoopModeRef 代表 RunLoop 的运行模式
  • 一个RunLoop 包含 若干个Mode每个Mode 又包含若干个 Source0/Source1/Timer/Observer
  • RunLoop 启动时只能选择其中一个Mode,作为 currentMode
  • 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入

    不同组的 Source0/Source1/Timer/Observer 能分隔开来,互不影响

  • 如果 Mode 里没有任何 Source0/Source1/Timer/ObserverRunLoop 会立马退出
目前已知的 5 种 Mode:
  • kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
  • UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  • UIInitializationRunLoopMode:在刚启动 App 时进入的第一个 Mode,启动完成后就不再使用
  • GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
  • kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode,包含 kCFRunLoopDefaultModeUITrackingRunLoopMode

CommonModes

一个 Mode 可以将自己标记为Common属性(通过将其 ModeName 添加到 RunLoopcommonModes 中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 Common 标记的所有 Mode 里。

_sources0 和 _sources1

  • Sources0: 触摸事件处理,performSelector: onThread:,非基于Port的
  • Sources1: 基于Port的线程间通信,系统事件捕捉

_timers

定时执行的定时器,底层基于使用 mk_timer 实现,受 RunLoop 的 Mode 影响(GCD的定时器不受 RunLoop 的Mode 影响);当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。如果线程阻塞或者不在这个Mode下,触发点将不会执行,一直等到下一个周期时间点触发。

timer 和 sources1(也就是基于 port 的 sources)可以反复使用,比如 timer 设置为 repeat,port 可以持续接收消息,而 sources0 在一次触发后就会被 runloop 移除。

_observers

  • 用于监听 RunLoop 的状态
  • UI刷新(BeforeWaiting)
  • AutoreleasePool(BeforeWaiting)
监听方法
// 创建Observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    switch (activity) {
        case kCFRunLoopEntry: {
            CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
            NSLog(@"kCFRunLoopEntry - %@", mode);
            CFRelease(mode);
            break;
        }
        case kCFRunLoopExit: {
            CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
            NSLog(@"kCFRunLoopExit - %@", mode);
            CFRelease(mode);
            break;
        }
        default:
            break;
    }
});
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  // 包含上面所有状态
};

RunLoop 运行逻辑

RunLoop 运行逻辑
查看源码
void CFRunLoopRun(void) {   /* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled);

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode);

RunLoop 休眠的实现原理

RunLoop 休眠的实现原理

相关文章

网友评论

      本文标题:RunLoop

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