RunLoop整理

本文在下面两篇文章的基础上,进行了理解和梳理。html

深刻理解RunLoop - ibiremebash

[转]iOS 事件处理机制与图像渲染过程网络

1 RunLoop是什么

RunLoop,是一个消息处理模式。并发

  1. 只要用户、系统没主动杀掉程序,RunLoop就让程序一直活着,而且节省资源消耗。
  2. 运行的机制,简单说就是,有事就作,没事就歇。
  3. 他经过mac port等,使他能够从休眠状态被唤醒。

2 生命周期

CFRunLoopObserverRef 是观察者,每一个 Observer 都包含了一个回调(函数指针),当 RunLoop 的状态发生变化时,观察者就能经过回调接受到这个变化。能够观测的时间点有如下几个:函数

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
};
复制代码

2-1 source0和source1

处理如UIEvent,CFSocket这类事件,须要手动触发。 触摸事件实际上是Source1接收系统事件后在回调 __IOHIDEventSystemClientQueueCallback() 内触发的 Source0,Source0 再触发的 _UIApplicationHandleEventQueue()。 Source0必定是要唤醒runloop及时响应并执行的,若是runloop此时在休眠等待系统的 mach_msg事件,那么就会经过source1来唤醒runloop执行oop

这样,上图如今就能够看明白啦ui

  1. source0如UI事件、网络事件的来源,都是source1建立
  2. 因此RunLoop的一次循环,先把全部Timer、source0事件解决掉,其间由于不响应source1和Timer,所以不会产生新的source0事件。
  3. 上图第4步结束后,才去检查有没有新的source1事件,若是就休眠。
  4. 只有source一、Timer、dispatch block能够唤醒RunLoop,而且谁唤醒就只处理谁。(其中source1会产生source0事件,再由RunLoop处理)

补充一个图this

3 RunLoop结构、模式,以及与线程的关系

3-1 与线程的关系

子线程,默认没有RunLoop,这样默认状况下,子线程执行完,就结束。(符合预期)spa

  1. 一一对应
  2. 延迟建立
  3. 结束销毁
  4. 私有(mainThread的RunLoop除外)

3-2 RunLoop内部结构

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;	/* locked for accessing mode list */
    __CFPort _wakeUpPort;	// used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData; // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};


struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;	/* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};
复制代码
  1. Observer 能够经过建立本身的Observer,监听RunLoop自己的生命周期

3-3 Mode指定、Mode切换

  1. mode一次只能指定一个
  2. 切换时,先退出前一个Loop,切换Model,进入另外一个Model。
  3. CommonModes

4 RunLoop和AutoReleasePool的关系

  1. 进入RunLoop时,建立AutoReleasePool
  2. beforeWaiting时,由于全部事都处理完了,因此释放旧池,并建立新池。(这里没有延后建立新池,多是由于如今最空,先建立出来,由于是高频使用的)
  3. 线程退出时,也就是RunLoop即将销毁时,释放AutoReleasePool。

5 RunLoop和GCD的关系

  1. 对于主线程中的task queue,至关于加入到了main RunLoop的dispatch block线程

  2. 对于并发队列中的task,至关于加入到了子线程RunLoop的dispatch block中,若是子线程尚未RunLoop会自动建立。

dispatch_after 3秒后交给RunLoop,可是RunLoop何时处理,就不必定了。

6 RunLoop和AFN2.0的关系

建立了一个本身的RunLoop(蓝色),并添加了一个空的port,用于避免RunLoop结束。

相关文章
相关标签/搜索