iOS原理(七)----runloop
runloop:顾名思义,运行循环,在程序运行过程中循环做一些事情,保持程序的持续运行,处理App中的各种事件(比如触摸事件、定时器事件等),节省CPU资源,提高程序性能:该做事时做事,该休息时休息.包括定时器(Timer),PerformSelector, GCD Async Main Queue,事件响应、手势识别、界面刷新,网络请求, AutoreleasePool.
- 每条线程都有唯一的一个与之对应的RunLoop对象
- RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
- 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建
- RunLoop会在线程结束时销毁
- 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
RunLoop的五个类: CFRunLoopRef, CFRunLoopModeRef, CFRunLoopSourceRef, CFRunLoopTimerRef, CFRunLoopObserverRef.
其中CFRunloop结构如下:
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock;
__CFPort _wakeUpPort;
Boolean _unused;
volatile _per_run_data *_perRunData;
pthread_t _pthread; // 线程
uint32_t _winthread;
CFMutableSetRef _commonModes; // 共用模式
CFMutableSetRef _commonModeItems; // 共用items
CFRunLoopModeRef _currentMode; // 当前模式
CFMutableSetRef _modes; // 所有的模式
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
CFRunLoopMode结构如下:
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFStringRef _name;
Boolean _stopped;
char _padding[3];
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
uint64_t _timerSoftDeadline;
uint64_t _timerHardDeadline;
};
- 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会立马退出
其中有两种常用的Mode:
- kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
RunLoop的运行流程:
- Source0:触摸事件处理, performSelector:onThread:.
- Source1:基于Port的线程间通信,系统事件捕捉.
- Timers: NSTimer,performSelector:withObject:afterDelay:
- Observers:用于监听RunLoop的状态, UI刷新(BeforeWaiting), Autorelease pool(BeforeWaiting).
01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop
RunLoop的应用:
- 控制线程生命周期(线程保活,通常我们频繁需要子线程处理事情,让线程不要重复创建)
- 解决NSTimer在滑动时停止工作的问题
- 监控应用卡顿
- 性能优化