runloop探究

669 阅读11分钟

runloop前言

同runtime运行时,runloop为一个对象,为一个do...while运行循环,其用来保持程序的持续运行,处理程序中的各种事件,包括触摸、定时器、消息等,因此其作用不言而喻 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);
}

runloop中一共有五个Ref结构:

CFRunLoopRef、CFRunLoopModeRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObserverRef

runloopRef结构如下所示,其中有所在线程,持有的多个modes等

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef; //CFRunLoopRef

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopSource * CFRunLoopSourceRef;//CFRunLoopSourceRef

typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopObserver * CFRunLoopObserverRef; //CFRunLoopObserverRef

typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef; //CFRunLoopTimerRef

typedef struct __CFRunLoopMode *CFRunLoopModeRef; //CFRunLoopModeRef

//CFRunLoopRef的结构体数组
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;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

runloop与线程的关系

runloop依赖于线程而创建的,也就意味着线程存活,runloop才会存活,每一个线程都有唯一一个runloop与其对应,一般创建线程就创建好了,唯一区别主线程的runloop默认开启,子线程的runloop默认不开启

//通过main runloop的获取可以看到其实根据线程创建的
CFRunLoopRef CFRunLoopGetMain(void) {
    CHECK_FOR_FORK();
    static CFRunLoopRef __main = NULL; // no retain needed
    if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}

runloop与其说是保持程序持续运行,不如说保持该线程的持续运行更为贴切,所以子线程如果不主动开启runloop则执行完毕当前任务,该线程则会结束运行

 //下面创建一个子线程,运行ruloop,与关闭线程的演示
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      [[NSRunLoop currentRunLoop] run];
      if (isStop) {
      	  [NSThread exit];
      }
  });

下图引用官方给的runloop的工作情况,其展现了Runloop在线程中的作用:从input source和timer source接受事件,然后在线程中处理事件

runloop的item

runloop主要处理下面6种类型的事件,也被成为runloop的item,即

// main  dispatch queue GCD主队列
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
//Observer事件,runloop中状态变化时进行通知。

// Block事件,非延迟的NSObject PerformSelector立即调用,dispatch_after立即调用,block回调
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__

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

// source1,处理系统内核的mach_msg事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__

// Timer事件,延迟的NSObject PerformSelector,延迟的dispatch_after,timer事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__

例如主队列、timer、block的调用:

//主队列
dispatch_async(dispatch_get_main_queue(), ^{   
});
//timer
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {}];
//block
void (^block)(void) = ^ {
};
block();

其中block的item结构如下所示,当需要执行的时候,会调用其block

struct _block_item {
    struct _block_item *_next;
    CFTypeRef _mode;    // CFString or CFSet
    void (^_block)(void);
}; //blockItem

runLoopMode

前面知道了runloop与线程为一对一的关系,并且runloop主要响应6中事件,即为其item,此外,每个runloop对象对应多个runLoopMode,每个runLoopMode表示着一种运行模式,如下所示:

kCFRunLoopDefaultMode: App的默认 Mode,通常主线程是在这个 Mode 下运行的。

UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。

UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。

GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到。

kCFRunLoopCommonModes: 这是一个占位的 Mode,当设置这个mode时,表示上面的所有mode模式下,事件都能正常响应。

runloop其运行模式会随着使用场景而切换,默认为kCFRunLoopDefaultMode下运行,当滚动视图滑动时mode会切换到UITrackingRunLoopMode中去

此外,每个runLoopMode都对应着多个item,例如:source(source0、source1)、observers、timers等,为一对多的关系

mode的结构体定义为CFRunLoopModeRef,源码如下所示:

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;    /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    //下面为runloop的items
    CFMutableSetRef _sources0; //source源集合set
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers; //observer源集合Array
    CFMutableArrayRef _timers; //timer源集合Array
    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 */
};

因此,runloop、线程、mode、source、timer、observer其关系如下图所示

通过__CFRunLoopDoBlocks源码分析runloopMode的运行逻辑

经过CFRunLoopRun后,会走向CFRunLoopRunSpecific方法,在走到__CFRunLoopRun,这里面讲了runloop实际运行逻辑,各种item事件的执行,我们这里通过block的事件代码,来分析runloopMode的item执行过程

block的执行代码__CFRunLoopDoBlocks,如下所示

static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
    if (!rl->_blocks_head) return false;
    if (!rlm || !rlm->_name) return false;
    Boolean did = false;
    struct _block_item *head = rl->_blocks_head;
    struct _block_item *tail = rl->_blocks_tail;
    rl->_blocks_head = NULL;
    rl->_blocks_tail = NULL;
    //获取所有的mode
    CFSetRef commonModes = rl->_commonModes;
    CFStringRef curMode = rlm->_name;
    __CFRunLoopModeUnlock(rlm);
    __CFRunLoopUnlock(rl);
    //获取item的的指针,保存上一个和头指针,可以看出其集合为链表数组
    struct _block_item *prev = NULL;
    struct _block_item *item = head;
    //遍历item,毕竟一个runloop中有多个mode
    while (item) {
        struct _block_item *curr = item; //保存当前item,下一轮是为上一个item
        //item指向下一个元素,方便遍历
        item = item->_next;
        Boolean doit = false;
        if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
            // timer 加入的mode 和 我们现在runloop的mode 相等
            // curr->_mode = kCFRunLoopCommonModes 相等
            // mode中存在当前时间
            达到以上条件即可执行doit为真
            doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        } else {
        	//是否是包含关系的判断,实际逻辑与上面的一致
            doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        }
        if (!doit) prev = curr;
        //满足条件
        if (doit) {
            if (prev) prev->_next = item;
            if (curr == head) head = item;
            if (curr == tail) tail = prev;
            void (^block)(void) = curr->_block; //获取item的block
            //清空modes和items,循环
            CFRelease(curr->_mode);
            free(curr);
            //执行当前block
            if (doit) {
                __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
                did = true;
            }
            //释放当前block
            Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
        }
    }
    __CFRunLoopLock(rl);
    __CFRunLoopModeLock(rlm);
    if (head) {
        tail->_next = rl->_blocks_head;
        rl->_blocks_head = head;
        if (!rl->_blocks_tail) rl->_blocks_tail = tail;
    }
    return did;
}

以timer为例我们都会添加一个时间到当前runloop中,其中可以选择为defaultMode还是commonMode,都会调用__CFRunLoopAddItemToCommonModes方法来添加新的mode和item

static void __CFRunLoopAddItemToCommonModes(const void *value, void *ctx) {
    CFStringRef modeName = (CFStringRef)value;
    CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
    CFTypeRef item = (CFTypeRef)(((CFTypeRef *)ctx)[1]);
    if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
        CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
    } else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
        CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
    } else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
        CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
    }
}

void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return;
    if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
    __CFRunLoopLock(rl);
    if (modeName == kCFRunLoopCommonModes) {
        CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
        if (NULL == rl->_commonModeItems) {
            rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
        }
        //加入item
        CFSetAddValue(rl->_commonModeItems, rlt);
        if (NULL != set) {
            CFTypeRef context[2] = {rl, rlt};
            /* add new item to all common-modes */
            // timer -- items()
            //遍历set递归添加
            CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
            CFRelease(set);
        }
    } else {
        ...
    }
    __CFRunLoopUnlock(rl);
}

CFRunLoopTimerRef

CFRunLoopTimerRef对象,CFRunLoopTimerRef其结构如下所示

struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;	//当前的runloop
    CFMutableSetRef _rlModes;	//mode集合
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;        /* immutable */ 时间间隔
    CFTimeInterval _tolerance;          /* mutable */
    uint64_t _fireTSR;            /* TSR units */
    CFIndex _order;            /* immutable */
    CFRunLoopTimerCallBack _callout;    /* immutable */ 回调
    CFRunLoopTimerContext _context;    /* immutable, except invalidation */
};

NSTimer对象的底层就是CFRunLoopTimerRef实现的,其模式也是会受到mode的切换所倾影响的,例如,使用下面这段代码

[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
      NSLog(@"123");
  }];

其生成的源码如下所示

 (void)createCFTimer {
    CFRunLoopTimerContext context = {
        0,
        ((__bridge void *)self),
        NULL,
        NULL,
        NULL
    };
    CFRunLoopRef rlp = CFRunLoopGetCurrent();//获取当前线程的runloop对象
    /**
     创建CFRunLoopTimerRef对象
     参数一:用于分配对象的内存
     参数二:在什么是触发 (距离现在)
     参数三:每隔多少时间触发一次
     参数四:未来参数
     参数五:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
     参数六:回调,比如触发事件,我就会来到这里
     参数七:上下文记录信息
     */
    CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 1, 0, 0, runLoopTimerCallBack, &context);
    //添加到当前runloop当中,默认为kCFRunLoopDefaultMode
    CFRunLoopAddTimer(rlp, timerRef, kCFRunLoopDefaultMode);
}
//回调方法
void runLoopTimerCallBack(CFRunLoopTimerRef timer, void *info){
}

CFRunLoopObserverRef

其对象如下所示

struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities;        /* immutable */
    CFIndex _order;            /* immutable */
    CFRunLoopObserverCallBack _callout;    /* immutable */ 回调
    CFRunLoopObserverContext _context;    /* immutable, except invalidation */会话
};

其活动状态activityes(CFRunLoopActivity)如下所示,其标识着runloop运行到某个阶段的状态,可以参考runloop的运行过程结合理解

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry , // 进入 loop
    kCFRunLoopBeforeTimers , // 即将触发 Timer 回调
    kCFRunLoopBeforeSources , // 即将触发 Source0 回调
    kCFRunLoopBeforeWaiting , // 等待 mach_port 消息,进入休眠
    kCFRunLoopAfterWaiting ), // 唤醒,接收 mach_port 消息
    kCFRunLoopExit , // 退出 loop
    kCFRunLoopAllActivities  // loop 所有状态改变
}

observer为一个观察者,观察到runloop的状态改变,并回调指定函数,实现如下所示

- (void)createCFObsever {
	//创建Observer会话层
    CFRunLoopObserverContext context = {
        0,
        ((__bridge void *)self),
        NULL,
        NULL,
        NULL
    };
    //获取当前runloop
    CFRunLoopRef rlp = CFRunLoopGetCurrent();
    /**
     参数一:用于分配对象的内存
     参数二:你关注的事件 CFRunLoopActivity类型
     参数三:CFRunLoopObserver是否循环调用
     参数四:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
     参数五:回调,比如触发事件,我就会来到这里
     参数六:上下文记录信息
     */
    CFRunLoopObserverRef observerRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, runLoopObserverCallBack, &context);
    //加入到当前runloop中,模式为kCFRunLoopDefaultMode
    CFRunLoopAddObserver(rlp, observerRef, kCFRunLoopDefaultMode);
}
//回调
void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
}

CFRunLoopSourceRef

CFRunLoopSourceRef为源,分为source0和source1,其为一个联合体结构(共享一片内存),表示source0和source1他们结构相同,根据其应用分为中,结构如下所示:

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;            /* immutable */
    CFMutableBagRef _runLoops;
    union {
        CFRunLoopSourceContext version0;    /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;    /* immutable, except invalidation */
    } _context;
};

source0

处理App内部事件、APP自己负责管理(触发) 例如:UIEvent CFSocket

- (void)source0 {
	//创建source会话层,最后三个参数分别为,待处理、取消、处理事件
    CFRunLoopSourceContext context = {
        0,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        schedule,
        cancel,
        perform,
    };
    /**
     
     参数一:传递NULL或kCFAllocatorDefault以使用当前默认分配器。
     参数二:优先级索引,指示处理运行循环源的顺序。这里我传0为了的就是自主回调
     参数三:为运行循环源保存上下文信息的结构
     */
    CFRunLoopSourceRef source0 = CFRunLoopSourceCreate(CFAllocatorGetDefault(), 0, &context);
    CFRunLoopRef rlp = CFRunLoopGetCurrent();
    // source runloop指定了mode,此时我们source就进入待绪状态
    CFRunLoopAddSource(rlp, source0, kCFRunLoopDefaultMode);
    // 一个执行信号,标记为待处理
    CFRunLoopSourceSignal(source0);
    // 唤醒run loop防止沉睡状态,处理事件
    CFRunLoopWakeUp(rlp);
    // 取消 移除
    CFRunLoopRemoveSource(rlp, source0, kCFRunLoopDefaultMode);
    CFRelease(rlp);
}

oid schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode){
    NSLog(@"准备工作");
}

void perform(void *info){
    NSLog(@"处理事件");
}

void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode){
    NSLog(@"取消了");
}

source1

包含一个 mach_port和一个回调(函数指针)

被用于通过内核和其他线程相互发送消息 NSPort通信这里就不介绍了

runloop运行过程

上面介绍了runloop的各种ref对象,以及与线程、mode、source、timer、observer等之间的对应关系,部分item的的获取以及使用(__CFRunLoopDoBlocks方法里面的item事件遍历)等

下面分析一下runloop源码,看看干了些什么

前面已经说了,主要事件为__CFRunLoopRun函数内部实现,由于内部逻辑非常长,这里将主要代码逻辑拿出查看(部分删除)

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    
    int32_t retVal = 0;
    do {
        
        /// 通知 Observers: 即将处理timer事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        /// 通知 Observers: 即将处理Source事件
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
        
        /// 处理Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        /// 处理sources0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        
        /// 处理sources0返回为YES
        if (sourceHandledThisLoop) {
            /// 处理Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        
        /// 判断有无端口消息(Source1)
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            /// 处理消息
            goto handle_msg;
        }
        
        /// 通知 Observers: 即将进入休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        __CFRunLoopSetSleeping(rl);
        
        /// 等待被唤醒
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
        
        
        // user callouts now OK again
        __CFRunLoopUnsetSleeping(rl);
        
        /// 通知 Observers: 被唤醒,结束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        
    handle_msg:
        if (被Timer唤醒) {
         	CFRUNLOOP_WAKEUP_FOR_TIMER();
            /// 处理Timers
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        } else if (被GCD唤醒) {
        	CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            /// 处理gcd
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        } else if (被Source1唤醒) {
	        CFRUNLOOP_WAKEUP_FOR_SOURCE();
            /// 被Source1唤醒,处理Source1
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
        }
        
        /// 处理block
        __CFRunLoopDoBlocks(rl, rlm);
        
        
        if (sourceHandledThisLoop && stopAfterHandle) {
            retVal = kCFRunLoopRunHandledSource;
        } else if (timeout_context->termTSR < mach_absolute_time()) {
            retVal = kCFRunLoopRunTimedOut;
        } else if (__CFRunLoopIsStopped(rl)) {
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        } else if (rlm->_stopped) {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
            retVal = kCFRunLoopRunFinished;
        }
        
    } while (0 == retVal);
    
    return retVal;
}

通过上面代码,可以的得出runloop的运行,经过了如下步骤

1.通知Observer:即将进入loop

2.通知Observer:将要处理timer事件

3.通知Observer:将要处理Source0

4.处理blocks

5.处理source0

6.如果有source1,跳转到第10步骤

7.通知Observer:即将进入休眠

8.休眠,等待被唤醒

9.通知Observer:线程刚被唤醒

10.处理唤醒时收到的消息,跳回到步骤2

11.通知Observer:即将退出runloop

监听runloop回调

可以通过下面的方法监听runloop的执行过程

// 添加一个监听者
- (void)addRunLoopObserver {
    // 1. 创建监听者
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault,
        kCFRunLoopAllActivities, YES, 0, 
        ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"进入RunLoop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即将处理Timer事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即将处理Source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即将休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"被唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"退出RunLoop");
                break;
            default:
                break;
        }
    });

    // 2. 添加监听者
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
}

runloop的应用

通过上面流程可以了解,runloop会随着使用场景切换mode

timer使用注意:

因此使用timer时需要注意,根据需要将timer放到相应的mode中去,如果想要随时运行,那么可以设置mode为commoneMode,如果不那么注重时间可以加入默认mode

图片加载优化:

平时异步加载图片一般不会卡顿,当在加载大图时,会渲染比较慢(此过程一定是在主线程、滑动也同理),当滚动视图快速滑动大图时则会卡顿,因此可以利用runloop的defalutMode模式中渲染图片,避免快速滑动卡顿问题(注意此过程是设置image的过程不是下载的过程)

监控主线程卡顿:

根据runloop的运行流程,在主线程添加Observer监测Runloop各种状态变化,正常来说一般runloop不是在处理事件的路上就是睡眠,一旦发现处于睡眠前的kCFRunLoopBeforeSources状态或者唤醒后的状态kCFRunLoopAfterWaiting,且在设置的时间阈值内一直没有变化(即:卡在了唤醒之后处理source之前,步骤8->4之间,不是4->8),即可认定为卡顿。

自己可以尝试一下实现吧