什么是Runloop
?
Runloop
是通过内部维护的事件循环来对事件和消息进行管理的一种机制。当没有消息需要处理的时候,线程进入休眠以避免占用资源,有消息需要处理时,立即被唤醒。
runloop
循环不是单独的do-while
循环,而是发生一个用户态到内核态切换,以及内核态到用户态切换。它维护的事件循环可以用来不断的处理消息和事件,当没有消息和事件需要处理时会从用户态切换到内核态,由此可以用来休眠线程,避免资源占用。当有消息需要处理时会从内核态切换到用户态,当前线程会被唤醒,所以状态切换才是runloop
的关键。
main
函数为何能保持不退出?
在main
函数中,会调用UIApplicationMain
函数,在内部会启动主线程的Runloop
,可以不断的接收消息,比如点击屏幕事件,滑动列表以及处理网络请求的返回等接收消息后对事件进行处理,处理完之后,就会继续等待。
iOS
中提供了两套Runloop
接口,一个是NSRunLoop
基于Objective-C
,在Foundation
框架中,另一个是CFRunLoopRef
基于C
,在CoreFoundation
中。而NSRunLoop
是对CFRunLoopRef
的封装,两者接口基本都是对应的。CFRunLoopRef runloop = [nsrunloop getCFRunLoop]
可以获取对应的CFRunLoopRef
。通过一个表格来对比一下:
特征 | NSRunLoop | CFRunLoopRef |
---|---|---|
所属框架 | Objective-C/Foundation | C/CoreFoundation |
获取Runloop | [NSRunLoop currentRunLoop] [NSRunLoop mainRunLoop] |
CFRunLoopGetCurrent() CFRunLoopGetMain() |
Source事件 | addPort:forMode: removePort:forMode: |
CFRunLoopAddSource(...) CFRunLoopRemoveSource(...) |
Timer事件 | addTimer:forMode: | CFRunLoopAddTimer(...) |
Observer事件 | CFRunLoopAddObserver(...) CFRunLoopRemoveObserver(...) |
1. __CFRunLoop相关基本数据结构
struct __CFRunLoop {
...
pthread_t _pthread;//对应的线程
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
...
};
__CFRunLoop
是runloop
本身:typedef struct __CFRunLoop *CFRunLoopRef
。__CFRunLoop
对应多个__CFRunLoopMode
。
struct __CFRunLoopMode {
...
CFStringRef _name;
CFMutableSetRef _sources0;//
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
...
};
__CFRunLoopMode
是runloop的运行模式:typedef struct __CFRunLoopMode *CFRunLoopModeRef
。每一个__CFRunLoopMode
又包含多个_sources0、_sources1、_observers、_timers事件。_sources0:非基于Port的,也就是用户主动发出的事件。_sources1:基于Port的,也就是系统内部的消息事件。_observers:观察者。
_timers:定时器事件。
相关的类的成员变量与关系:
2.NSRunLoop的创建
[NSRunLoop mainRunLoop]
对应底层的CFRunLoopGetMain()
,[NSRunLoop currentRunLoop]
对应底层的CFRunLoopGetCurrent()
,内部都是通过CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t)
获取的runloop
,分别传入pthread_main_thread_np()
和pthread_self()
也就是主线程和当前线程的id。
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
//如果外部传入无效的0,则将主线程ID赋值给t。
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
//如果__CFRunLoops为空,则创建主线程对应的runloop
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//__CFRunLoopCreate做线程的初始化
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
// 将mainLoop保存到dict中,以线程id为key,mainLoop为value
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
//将dict中的内容复制到__CFRunLoops地址上
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
//通过线程id获取runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
//如果获取到的为空,则直接创建
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//如果创建后还不能获取到则使用刚才创建的,并将newLoop保存到__CFRunLoops中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&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;
}
在__CFRunLoopCreate
中通过_CFRuntimeCreateInstance
实例华再进行其他变量的一些初始化。其中loop->_pthread = t;
将线程id
绑定到了runloop
上。runloop
通过线程id
去查找,如果没有则进行创建并将线程id
绑定到runloop
上,通过这个规则我们知道:线程与runloop
一一对应;线程不一定都有runloop
,首个runloop
创建时会检查__CFRunLoops
是否为空,为空则先创建主线程的runloop
,再创建指定线程的runloop
。