一、Runloop的简单介绍
Runloop 是在程序运行过程中循环做一些事情。比如应用在:定时器(NSTimer)、PerformSelector;GCD Async Main Queue;事件响应、手势识别、界面刷新、网络请求、AutoreleasePool
- RunLoop的基本作用:保持程序的持续运行;处理App中的各种事件(如触摸事件、定时器事件等);节省CPU资源,提高程序性能:该做事时做事,该休息时休息
二、RunLoop对象的获取
- 2.1、iOS中有2套API来访问和使用RunLoop
- Foundation:NSRunLoop
- Core Foundation:CFRunLoopRef
- 2.2、NSRunLoop和CFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装
CFRunLoopRef是开源的 - 2.3、RunLoop与线程
- 每条线程都有唯一的一个与之对应的RunLoop对象
- RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
- 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
- RunLoop会在线程结束时销毁
- 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
- 2.4、获取RunLoop对象
- OC(Foundation)获取RunLoop对象
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象 [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
- C(Core Foundation)获取RunLoop对象
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象 CFRunLoopGetMain(); // 获得主线程的RunLoop对象
三、RunLoop相关的类
Core Foundation中关于RunLoop的5个类
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
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; };
- 3.1、CFRunLoopModeRef
- CFRunLoopModeRef代表RunLoop的运行模式
- 一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Source1/Timer/Observer
- RunLoop启动时只能选择其中一个Mode,作为currentMode
- 如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入
- 不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响
- 如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出
- CFRunLoopModeRef 2种Mode
kCFRunLoopDefaultMode
(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行UITrackingRunLoopMode
:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
3.2、RunLoop的运行逻辑
- Source0
触摸事件处理
performSelector:onThread: - Source1
基于Port的线程间通信
系统事件捕捉 - Timers
NSTimer
performSelector:withObject:afterDelay: - Observers
用于监听RunLoop的状态
UI刷新(BeforeWaiting)
Autorelease pool(BeforeWaiting)
3.3、RunLoop休眠的实现原理
四、RunLoop在实际开中的应用 下面练习的demo
- 4.1、控制线程生命周期(线程保活)
- (1)OC核心代码实现保活 : 看demo里面的类
JKCPermenantThread
self.innerThread = [[MJThread alloc] initWithBlock:^{ // 线程保活 // 往RunLoop里面添加 Source\Timer\Observer [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode]; while (weakSelf && !weakSelf.isStopped) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } }];
- (2)C核心代码实现保活:看demo里面的类
ThreadCViewController
self.innerThread = [[JKThread alloc] initWithBlock:^{ NSLog(@"begin----"); // 创建上下文(要初始化一下结构体) CFRunLoopSourceContext context = {0}; // 创建source CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); // 往Runloop中添加source CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode); // 销毁source CFRelease(source); // 启动 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false); // while (weakSelf && !weakSelf.isStopped) { // // 第3个参数:returnAfterSourceHandled,设置为true,代表执行完source后就会退出当前loop // CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true); // } NSLog(@"end----"); }];
- 4.2、解决NSTimer在滑动时停止工作的问题(我在做一个cell上动画的时候用了NSRunLoop来防止tableview滑动的时候cell上的动画暂停)
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(clickTimeIndexPath) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; /* UITrackingRunLoopMode和NSDefaultRunLoopMode是真正存在的模式 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode]; NSRunLoopCommonModes:n并不是一个真的模式,它只是一个标记 timer 能在 _commandModes 数组中存放的模式下工作 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; */
五、RunLoop的几个面试题
- 5.1、讲讲 RunLoop,项目中有用到吗?(你可以回答
四
里面的任意一个)
答:我用到了:解决NSTimer在滑动时停止工作的问题(我在做一个cell上动画的时候用了NSRunLoop来防止tableview滑动的时候cell上的动画暂停) - 5.2、runloop内部实现逻辑?
答:看上面的3.2、RunLoop的运行逻辑
- 5.3、runloop和线程的关系?
答:看上面的2.3、RunLoop与线程
- 5.4、timer 与 runloop 的关系?
答: timer 是运行在runloop里面的,runloop控制 timer什么时候执行 - 5.5、程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?
答:看上面的4.2,也就是要添加NSRunLoopCommonModes
标记 - 5.6、runloop 是怎么响应用户操作的, 具体流程是什么样的?
答: 由 source1捕获触摸或者响应事件,再由 source0去处理触摸或者响应事件 - 5.7、说说runLoop的几种状态
5.8、runloop的mode作用是什么?
答:runloop的mode 常用的有两种mode: NSDefaultRunLoopMode
与UITrackingRunLoopMode
,在某一种mode下执行自己的source0、source1、timer,不同mode下互不影响,分工明确,使用起来更灵活、流畅。