这次学习 的内容是 runloop
1.runloop 是什么
2.runloop 的作用
3.runloop 和 线程之间的关系
4.runloop 源码分析 runloop对象和mode
5.runloop 源码分析 CFRunloopSourceRef & CFRunloopObserveRef
6.runloop 源码分析
runloop的源码大概有3900多行
Runloop的应用
- 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
- GCD主队列:CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE
- observer源:CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION
1.runloop 是什么
- runloop 是一个运行循环 同时它也是一个对象(对象里面会提供很多东西)
- 处理app中的各种事件 触摸·定时器·performSelector
- 程序会进入do...while 循环 循环处理一些事情
/** runloop 源码看 就是个 do while 循环 入口函数 程序进入do...while */
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
2.runloop 的作用
- 保持程序的持续运行
- 处理APP中的各种事件(触摸,定时器,performSelector)
- 节省cpu资源,提供程序的性能:该做事的时候做事,该休息就休息
3.runloop 和 线程之间的关系
- runloop与线程是 一一对应, 一个runloop 对应一个核心的线程,之所以说是核心,是因为runloop是可以嵌套,但是核心只能有一个,他们关系保存在全局的字典里面
/** 通过下面这段源代码就可以知道 --对应关系 */
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
2.runloop是专门来管理线程,当线程的runloop开启之后,线程会在执行完成之后进入休眠状态,有任务就会醒来去干活
3.runloop 在第一次获取时就会被创建,线程结束时会销毁
4.对主线程来说,runloop在程序启动的时候就会默认创建出来
5.对子线程来说,runloop是懒加载,只有在用到的时候才会创建,所以在子线程用定时器要注意:确保子线程的runloop被创建,不然定时器不会回调
4.runloop 源码分析 runloop对象和mode
mode == kCFRunLoopCommonModes
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);
}
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
// timer -- items()
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
}
//这里代码只是截取了一部分
}
这段代码是首先将timer 加入到 commonModes里面去
timer 最终会加入到 items里面
timer 和 runloop : timer一定要加入到某一个相应的mode里面去
通过底层源码知道 timer 就会加入到 items里面
runloopRun 调用的时候 里面就会有doblock,就是while循环当前的items 一层一层循环查找,直到找到了itme没有 会先进行doit判断,再是block调用
- (void)cfTimerDemo{
CFRunLoopTimerContext context = {
0,
((__bridge void *)self),
NULL,
NULL,
NULL
};
CFRunLoopRef rlp = CFRunLoopGetCurrent();
/**
参数一:用于分配对象的内存
参数二:在什么是触发 (距离现在)
参数三:每隔多少时间触发一次
参数四:未来参数
参数五:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
参数六:回调,比如触发事件,我就会来到这里
参数七:上下文记录信息
*/
CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 1, 0, 0, lgRunLoopTimerCallBack, &context);
CFRunLoopAddTimer(rlp, timerRef, kCFRunLoopDefaultMode);
/** 先创建 RunLoop上下文 和 当前CFRunLoop 然后创建CFRunLoopTimer 并调用加入到timer函数 */
}
void lgRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info){
NSLog(@"%@---%@",timer,info);
}
打印结果 <__NSCFTimer: 0x280b68d80>---<ViewController: 0x111d12700>
5.runloop 源码分析 CFRunloopSourceRef & CFRunloopObserveRef
先上代码
- (void)cfObseverDemo{
CFRunLoopObserverContext context = {
0,
((__bridge void *)self),
NULL,
NULL,
NULL
};
CFRunLoopRef rlp = CFRunLoopGetCurrent();
/**
参数一:用于分配对象的内存
参数二:你关注的事件
kCFRunLoopEntry=(1<<0),
kCFRunLoopBeforeTimers=(1<<1),
kCFRunLoopBeforeSources=(1<<2),
kCFRunLoopBeforeWaiting=(1<<5),
kCFRunLoopAfterWaiting=(1<<6),
kCFRunLoopExit=(1<<7),
kCFRunLoopAllActivities=0x0FFFFFFFU
参数三:CFRunLoopObserver是否循环调用
参数四:CFRunLoopObserver的优先级 当在Runloop同一运行阶段中有多个CFRunLoopObserver 正常情况下使用0
参数五:回调,比如触发事件,我就会来到这里
参数六:上下文记录信息
*/
CFRunLoopObserverRef observerRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, lgRunLoopObserverCallBack, &context);
CFRunLoopAddObserver(rlp, observerRef, kCFRunLoopDefaultMode);
}
void lgRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
NSLog(@"%lu-%@",activity,info);
}
2019-04-24 11:37:13.352235+0800 2-<ViewController: 0x105514210>
2019-04-24 11:37:13.352460+0800 4-<ViewController: 0x105514210>
2019-04-24 11:37:13.352661+0800 32-<ViewController: 0x105514210>
2019-04-24 11:37:13.353129+0800 64-<ViewController: 0x105514210>
2019-04-24 11:37:13.353419+0800 2-<ViewController: 0x105514210>
2019-04-24 11:37:13.353608+0800 4-<ViewController: 0x105514210>
2019-04-24 11:37:13.353743+0800 32-<ViewController: 0x105514210>
2019-04-24 11:37:14.306024+0800 64-<ViewController: 0x105514210>
2019-04-24 11:37:14.306992+0800 2-<ViewController: 0x105514210>
2019-04-24 11:37:14.307225+0800 4-<ViewController: 0x105514210>
2019-04-24 11:37:14.307430+0800 32-<ViewController: 0x105514210>
2019-04-24 11:38:00.005751+0800 64-<ViewController: 0x105514210>
2019-04-24 11:38:00.008181+0800 2-<ViewController: 0x105514210>
2019-04-24 11:38:00.008396+0800 4-<ViewController: 0x105514210>
2019-04-24 11:38:00.008604+0800 32-<ViewController: 0x105514210>
打印结果的时候回发现, 在后面相隔几十秒会跳动一下,这个时间时间段没事干,他会处于休眠模式,会让一些状态改变
observe总结: 说简单点observer就是监听runloop的状态
source 有 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
source1 包含一个mach_port 以及 回调函数指针 被用于通过内核和其他线程相互发送消息
小知识点
子线程runloop 默认不开启
timer 依赖于runloop
网友评论