0%

runloop

Runloop

简介

RunLoop是事件接收和分发机制的一个实现,是线程相关的基础框架的一部分,一个RunLoop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。

RunLoop本质是一个 do-while循环,没事做就休息,来活了就干活。与普通的while循环是有区别的,普通的while循环会导致CPU进入忙等待状态,即一直消耗cpu,而RunLoop则不会,RunLoop是一种闲等待,即RunLoop具备休眠功能。

RunLoop的作用

  • 保持程序的持续运行
  • 处理App中的各种事件(触摸、定时器、performSelector)
  • 节省cpu资源,提供程序的性能,该做事就做事,该休息就休息

源码分析

源码下载

runloop与线程

通常情况下获取runloop的两种方式:

1
2
3
4
// 主运行循环
CFRunLoopRef mainRunloop = CFRunLoopGetMain();
// 当前运行循环
CFRunLoopRef currentRunloop = CFRunLoopGetCurrent();

接下来看一下源码:

1
2
3
4
5
6
7
8
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
// 这是一个静态变量
static CFRunLoopRef __main = NULL; // no retain needed
// 没有获取到,则通过_CFRunLoopGet0函数去获取,参数是主线程
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}

查看一下_CFRunLoopGet0函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
// 如参数t不存在,则默认为主线程
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);

// 创建一个字典
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
// 创建mainLoop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());

// dict : key value
// 把main_thread和mainloop通过key-value的形式绑定
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);

if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}

CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}

// 从字典中通过线程获取run loop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
if (!loop) {
// 没有则创建
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
// 没有loop也要存,存的是新创建的。
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFSpinUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}

上面的代码可以看出,runloo只有两种类型,一种主线程的mainloop,还有就是其它runloop。

runloop的创建

接下来看runloop是怎么创建的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
CFRunLoopRef loop = NULL;
CFRunLoopModeRef rlm;
uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopTypeID, size, NULL);
// 如果loop为空,则直接返回NULL
if (NULL == loop) {
return NULL;
}
// runloop属性赋值
(void)__CFRunLoopPushPerRunData(loop);
__CFRunLoopLockInit(&loop->_lock);
loop->_wakeUpPort = __CFPortAllocate();
if (CFPORT_NULL == loop->_wakeUpPort) HALT;
__CFRunLoopSetIgnoreWakeUps(loop);
loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
loop->_commonModeItems = NULL;
loop->_currentMode = NULL;
loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
loop->_blocks_head = NULL;
loop->_blocks_tail = NULL;
loop->_counterpart = NULL;
loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
loop->_winthread = GetCurrentThreadId();
#else
loop->_winthread = 0;
#endif
rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
return loop;
}

里面又有了一个CFRunLoopRef,盲猜应该是结构体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct __CFRunLoop * 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;
CFTypeRef _counterpart;
};

从定义中可以得出,一个RunLoop有多个Mode,意味着一个RunLoop需要处理多个事务,即一个Mode对应多个Item,而一个item中,包含了timer、source、observer,如图:

mode类型

其中mode在苹果文档中提及的有五个,而在iOS中公开暴露出来的只有 NSDefaultRunLoopModeNSRunLoopCommonModes

NSRunLoopCommonModes 实际上是一个 Mode 的集合,默认包括 NSDefaultRunLoopModeNSEventTrackingRunLoopMode

  • NSDefaultRunLoopMode:默认的mode,正常情况下都是在这个mode
  • NSConnectionReplyMode
  • NSModalPanelRunLoopMode
  • NSEventTrackingRunLoopMode:使用这个Mode去跟踪来自用户交互的事件(比如UITableView上下滑动)
  • NSRunLoopCommonModes:伪模式,灵活性更好

source

  • Source0 表示 非系统事件,即用户自定义的事件
  • Source1 表示系统事件,主要负责底层的通讯,具备唤醒能力

Observer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
//进入RunLoop
kCFRunLoopEntry = (1UL << 0),
//即将处理Timers
kCFRunLoopBeforeTimers = (1UL << 1),
//即将处理Source
kCFRunLoopBeforeSources = (1UL << 2),
//即将进入休眠
kCFRunLoopBeforeWaiting = (1UL << 5),
//被唤醒
kCFRunLoopAfterWaiting = (1UL << 6),
//退出RunLoop
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};

mode对应的items

  • block:CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
  • timer:CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION
  • source0: CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION
  • source1: CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
  • 主队列:CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
    *observer: CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION

以Timer为例

在子线程创建的timer是没有办法一直执行的,而想让它继续执行,则需要添加到runloop中,并且run才行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
self.isStopping = NO;
NSThread *thread = [[NSThread alloc] initWithBlock:^{

// thread.name = nil 因为这个变量只是捕捉
// LGThread *thread = nil
// thread = 初始化 捕捉一个nil进来
NSLog(@"%@---%@",[NSThread currentThread],[[NSThread currentThread] name]);
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"~~hello word"); // 退出线程--结果runloop也停止了
if (self.isStopping) {
[NSThread exit];
}
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
}];

thread.name = @"lgcode.com";
[thread start];

我们看一下addTimer是怎么操作的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
oid 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);

// 重点 : kCFRunLoopCommonModes
if (modeName == kCFRunLoopCommonModes) {
//如果是kCFRunLoopCommonModes 类型
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;

if (NULL == rl->_commonModeItems) {
// modeItems是空,则创建一个defalut
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
//runloop与mode 是一对多的, mode与item也是一对多的
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
//执行
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
//如果是非commonMode类型
//查找runloop的模型
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm) {
if (NULL == rlm->_timers) {
CFArrayCallBacks cb = kCFTypeArrayCallBacks;
cb.equal = NULL;
rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
}
}
//判断mode是否匹配
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
__CFRunLoopTimerLock(rlt);
if (NULL == rlt->_runLoop) {
rlt->_runLoop = rl;
} else if (rl != rlt->_runLoop) {
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
return;
}
// 如果匹配,则将runloop加进去,而runloop的执行依赖于 [runloop run]
CFSetAddValue(rlt->_rlModes, rlm->_name);
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
__CFRepositionTimerInMode(rlm, rlt, false);
__CFRunLoopTimerFireTSRUnlock();
if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
// Normally we don't do this on behalf of clients, but for
// backwards compatibility due to the change in timer handling...
if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}

__CFRunLoopUnlock(rl);
}

主要目的就是把timer添加到对应的mode中。mode 和 item是一对多的关系,timer是item的一种。

__CFRunLoopRun

接下来上源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {

...
do {
...

__CFRunLoopUnsetIgnoreWakeUps(rl);

if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

__CFRunLoopDoBlocks(rl, rlm);

Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
...

//如果是timer
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
}

...

//如果是source1
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
if (rls) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
mach_msg_header_t *reply = NULL;
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
if (NULL != reply) {
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
#elif DEPLOYMENT_TARGET_WINDOWS
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
}
...

}while (0 == retVal);

...
}

__CFRunLoopDoTimers源码,主要是通过for循环,对单个timer进行处理。

1
2
3
4
5
6
7
8
9
10
11
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) {    /* DOES CALLOUT */
...
//循环遍历,做下层单个timer的执行
for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
// 执行timer
Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
timerHandled = timerHandled || did;
}
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// mode and rl are locked on entry and exit
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */
Boolean timerHandled = false;
uint64_t oldFireTSR = 0;

/* Fire a timer */
CFRetain(rlt);
__CFRunLoopTimerLock(rlt);
if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
oldFireTSR = rlt->_fireTSR;
__CFRunLoopTimerFireTSRUnlock();

__CFArmNextTimerInMode(rlm, rl);

__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
// 执行timer
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
CHECK_FOR_FORK();
if (doInvalidate) {
CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */
}
if (context_release) {
context_release(context_info);
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
__CFRunLoopTimerLock(rlt);
timerHandled = true;
__CFRunLoopTimerUnsetFiring(rlt);
}
}

在timer执行的位置打上断点,使用lldb -> bt命令查看调用栈:

timer的调用顺序

  1. 自定义的timer,设置Mode,并将其加入RunLoop中
  2. 在RunLoop的run方法执行时,会调用__CFRunLoopDoTimers执行所有timer
  3. 在__CFRunLoopDoTimers方法中,会通过for循环执行单个timer的操作
  4. 在__CFRunLoopDoTimer方法中,timer执行完毕后,会执行对应的timer回调函数

是针对timer的执行分析,对于observer、block、source0、source1,其执行原理与timer是类似的

runloop底层原理

1
2
3
4
5
6
7
8
void CFRunLoopRun(void) {    /* DOES CALLOUT */
int32_t result;
do {
// 1.0e10 : 科学计数 1*10^10,很大的值
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

runloop就是一个do-while循环。当stop或者执行完成之后,则退出循环。

看一下CFRunLoopRunSpecific的内部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);

//首先根据modeName找到对应mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);

// 通知 Observers: RunLoop 即将进入 loop。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);

// 内部函数,进入loop,seconds是一个很大的值
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

// 通知 Observers: RunLoop 即将退出。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

return result;
}

接下来又回到__CFRunLoopRun的代码,上面提到的逻辑只是针对timer的,这里详细的说明一下,使用伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//核心函数
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode){

//通过GCD开启一个定时器,然后开始跑圈
dispatch_source_t timeout_timer = NULL;
...
dispatch_resume(timeout_timer);

int32_t retVal = 0;

//处理事务,即处理items
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 (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
// 处理消息
goto handle_msg;
}


// 通知 Observers: 即将进入休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);

// 等待被唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);

// user callouts now OK again
__CFRunLoopUnsetSleeping(rl);

// 通知 Observers: 被唤醒,结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);


handle_msg:
if (被timer唤醒) {
// 处理Timers
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
}else if (被GCD唤醒){
// 处理gcd
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}else if (被source1唤醒){
// 被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的整体流程如下:

总结

引用

源码下载