RunLoop简介
运行循环,在程序运行过程中循环做一些事情(如接收消息、处理消息、休眠等待等);
RunLoop
是通过内部维护的事件循环来对事件/消息
进行管理的一个对象;RunLoop
不是一个简单的do...while
循环,它涉及到用户态
和内核态
之间的切换。
事件循环
事件循环就是对事件/消息进行管理,事件循环可以达到:
- 没有消息需要处理时,休眠线程以避免资源占用。从用户态切换到内核态,等待消息;
- 有消息需要处理时,立刻唤醒线程,回到用户态处理消息;
- 通过调用
mach_msg()
函数来转移当前线程的控制权给内核态/用户态
RunLoop的基本作用
保持程序的持续运行:如果没有Runloop,main()函数一执行完,程序就会立刻退出(main()函数中调用了UIApplicationMain函数,它的内部会启动主线程的Runloop)
处理App中的各种事件(比如触摸事件、定时器事件等)
节省CPU资源,提高程序性能;该做事时做事,该休息时休息
应用范畴
定时器、PerformSelector
GCD:dispatch_async(dispatch_get_main_queue(), ^{ });
事件响应、手势识别、界面刷新
网络请求
AutoreleasePool
实际开发中的应用
使用端口或自定义输入源与其他线程进行通信
在子线程上使用定时器
解决NSTimer在滑动时停止工作的问题
控制线程的生命周期,实现一个常驻线程
在Cocoa应用程序中使用任何performSelector…方法
监控应用卡顿
性能优化
数据结构
CFRunLoopRef
RunLoop
对象的底层就是一个CFRunLoopRef
结构体,它里面存储者:
1 | // CFRunLoop.h |
CFRunLoopModeRef
1 | // CFRunLoop.h |
CFRunLoopModeRef代表RunLoop的运行模式
一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0/Souce1/Timer/Obsesrver;
RunLoop启动时只能选择其中一个Mode,作为currentMode;
如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入,切换模式不会导致程序退出
不同Mode中的Source0/Souce1/Timer/Observer能分隔开来,互不影响
如果Mode里没有任何Source0/Souce1/Timer/Observer,RunLoop会立马退出
常见模式
NSDefaultRunLoopMode、UITrackingRunLoopMode、UIRunLoopCommonModes(通用模式,默认包含defaultMode和TrackingMode,该模式不是实际存在的一种模式,它只是一个特殊的标记,是同步Source0/Souce1/Timer/Observer到多个Mode中的技术方案。被标记为通用模式的Source0/Source1/Timer/Observer都会存放到多个Mode中的技术方案。被标记为通用模式Source0/Source1/Timer/Observer都会存放到_commonModeItems集合中,会同步这些Source0/Source1/Timer/Observer到多个Mode中)
CFRunLoopSourceRef
在RunLoop中有两个很重要的概念,一个是上面提到的模式,还有一个就是事件源。事件源分为输入源和定时器源两种:
输入源又分为Source0和Source1两种,以下__CFRunLoopSource中的共用体uinon中的version0和version1就分别对应Source0和Source1
1 | // CFRunLoop.h |
Source0和Source1的区别
CFRunLoopTimerRef
CFRunloopTImer
和NSTimer
是toll-free bridged的,可以相互转换;performSelector:withObject:afterDelay:
方法会创建timer并添加到RunLoop中。
1 | // CFRunLoop.h |
CFRunLoopObserverRef
作用
CFRunLoopObserverRef
用来监听RunLoop
的6种活动状态
1 | /* Run Loop Observer Activities */ |
- UI刷新(BeforeWaiting)
- Autorelease Pool(BeforeWaiting)
数据结构
1 | // CFRunLoop.h |
CFRunLoopObserverRef中的_activities
用来保存RunLoop
的活动状态。当RunLoop
的状态发生改变时,通过回调_callout
通知所有监听这个状态的Observer