RunLoop

2020-05-09  本文已影响0人  iVikings

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

RunLoop 应用范畴

RunLoop 基本作用

RunLoop 对象

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

NSRunLoopCFRunLoopRef 都代表着 RunLoop对象

RunLoop 与线程

获取 RunLoop 对象

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

RunLoop 的构成

RunLoop

结构体如下:

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

目前已知的 5 种 Mode:

CommonModes

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

_sources0 和 _sources1

_timers

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

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

_observers

监听方法
// 创建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 休眠的实现原理
上一篇 下一篇

猜你喜欢

热点阅读