一.基本概念及用途
1.runloop是什么?
1)字面意思:跑圈.
2)runloop是一个do while 循环,
3)保证程序持续运行,
4)处理App的各种事件(比如触摸,定时器,Selector事件)
5)节省cpu资源,提高程序性能.(休眠,唤醒)
2.runloop应用于什么?
1)使线程不被销毁,常驻内存
2)在runloop 即将休眠的时候做一些其他操作
3)在子线程中开启一个定时器
4)在子线程做一些长期的监控
3.runloop与线程关系
1)主线程runloop 默认创建开启
2)子线程的runloop需要手动创建
3)runloop 在线程中获取是创建,在线程结束时销毁
4)每条线程都有与之一一对应的runloop对象
二.runloop相关的5个类
1.runloop相关的5个类
CFRunLoopRef
: runloop对象
CFRunLoopMode
: runloop 的运行模式
CFRunLoopTimerRef
: runloop 的计时器对象
CFRunLoopObserverRef
: runloop 观察者
CFRunLoopSourceRef
: runloop 输入源,事件源
2.runloop对象获取
NSFoundation
[NSRunLoop currentRunLoop]
//获取当前线程的runloop 对象
[NSRunLoop mainRunLoop]
//获取当主线程的runloop 对象
Core Foundation
CFRunLoopGetCurrent()
//获取当前线程的runloop 对象
CFRunLoopGetMain()
//获取当主线程的runloop 对象
我们更多的使用是Core Foundation,因为Core Foundation可以获取到RunLoopObserver从而监听runloop 的运行状态
NSLog(@"%@-----%@",CFRunLoopGetCurrent(),[NSRunLoop currentRunLoop]);
//
//
3.runloopMode
runloop 的有5种运行模式,每次只能指定一个Mode
NSDefaultRunLoopMode
:App的默认Mode,通常主线程是在这个Mode下运行
UITrackingRunLoopMode
:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
NSRunLoopCommonModes
:这是一个占位用的Mode,不是一种真正的Mode
UIInitializationRunLoopMode
:在刚启动App时进入的第一个Mode,启动完成后不再使用
GSEventReceiveRunLoopMode
:接受系统事件的内部Mode,通常用不到
4.runloopTimer
顾名思义就是定时器的意思
NSTimer 受 runloop 影响,它只是跑在对应的mode 上(NSDefaultRunLoopMode
或者 UITrackingRunLoopMode
),如果想跑在两者上需要将定时器添加到NSRunLoopCommonModes
,所以我们用计时器的时候需要注意加在哪个Mode上.
** 另一种定时器 GCD
//.创建定时器
//第一个参数:source类型-> DISPATCH_SOURCE_TYPE_TIMER 代表定时器
//第二个参数:描述信息,线程ID
//第三个参数:更详细的信息
//第四个参数:决定在哪个线程中执行
//dispatch_source_t 带* 的结构体,需要强引用timer,否则不会执行
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//.设置定制器
//第一个参数:定时器对象
//第二个参数:从现在开始
//第三个参数:多久间隔
//第四个参数:精准度,绝对精准 0
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//.执行回调
dispatch_source_set_event_handler(self.timer, ^{
[self test];
});
//启动
dispatch_resume(self.timer);
相比timer :
1.不受runloop 影响
2.绝对精准
5.runloopObserver
runloopObserver:监听者,观察者
监听runloop运行状态,获取监听者需要Core Foundation
//.创建observer
//第一个参数:分配内存方式
//第二个参数:监听状态
//第三个参数:总是监听传YES
//第四个参数:优先级,总是传0
//第五个参数:监听回调
CFRunLoopObserverRef Observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry: //即将进入runloop
NSLog(@"即将进入runloop");
break;
case kCFRunLoopBeforeTimers: //即将处理timer
NSLog(@"即将处理timer");
break;
case kCFRunLoopBeforeSources: //即将处理source
NSLog(@"即将处理source");
break;
case kCFRunLoopBeforeWaiting: //即将进入休眠
NSLog(@"即将进入休眠");
break;
case kCFRunLoopAfterWaiting: //被唤醒
NSLog(@"被唤醒");
break;
case kCFRunLoopExit: //runloop退出
NSLog(@"runloop退出");
break;
default:
break;
}
});
//加入到当前runloop
CFRunLoopAddObserver(CFRunLoopGetCurrent(), Observer, kCFRunLoopDefaultMode);
[[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
CFRunLoopRun();
因为在子线程中开启runloop 至少需要一个timer 或者 source ,runloop 才会启动. addPort
是比添加timer 更好的方式
6.runloopSource
runloopSource:事件源,输入源
有两种source:
source0
: 非基于port,手动触发的事件
source1
: 基于port ,属于系统级的
三.看看代码
先看看runloop的源码
源码地址:https://opensource.apple.com/source/CF/CF-855.14/
找到CFRunLoop.c
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_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);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//将mainLoop 存入到字典中
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
//根据线程获取一个loop,__CFRunLoops是以一个子线程的字典 区别于 上面的 dict
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
if (!loop) {
//没有loop,创建一个
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//如果__CFRunLoops 没有与之子线程对应的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
__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;
}
四.运行图
以上是对runloop 的总结,对于子线程常驻问题,有一个好处是节省线程的创建销毁的开销,这个代码其实在 5.runloopObserver
中已经有了,以后有新的我会补充进来