1.什么是RunLoop
相当于一个do-while循环的对象,在循环中处理各种事件来保持程序运行,没有事件处理时线程会休眠,在有事件时才会唤醒线程来处理,节省CPU资源,提高效率。
一个线程对应一个runloop对象,主线程的是系统创建,子线程的需要自己创建,在第一次获取runloop时创建,在线程结束时销毁。
runloop保存在一个全局的dictionary里,key是线程,value是runloop。
2.RunLoop Mode-运行模式
做不同的事情会切换到不同的mode来执行
NSDefaultRunLoopMode:默认运行mode,主线程一般在这个mode运行,当用户停止操作时候会在这个mode,没有任务执行了就会休眠。
UITrackingRunLoopMode:界面跟踪mode,用于scrollview追踪滑动,保证界面滑动时不受其他mode影响。
NSRunLoopCommonModes:占位mode,不是真正的运行mode,会同时处理默认mode和UImode的事件。
以上是实际开放的mode。
UIInitializationRunLoopMode:在刚启动时第一个mode,启动完就不再使用了。
GSEventReceiveRunLoopMode:接收系统事件的内部mode,通常用不到。
3.RunLoop Source-事件源/输入源
sources0:非基于mach_port的,一般为内部的事件,如触摸事件、selector事件。
sources1:基于mach_port的内核事件,可以由系统内核或者其他进程或者线程主动唤醒。
像点击屏幕,由source0进行接收,分发到souces1处理。
4.定时源:就是定时器,NSTimer
子线程定时器默认不开启,如果在子线程中执行需要手动开启runloop。
timerWithTimeInterval是不加入到loop中。
scheduledTimerWithTimeInterval会默认加到默认mode中。
在滑动scrollview时会触发UImode,需要将timer加入到UImode或者commonmode,才能触发。
5.RunLoop Observer-观察者
监听runloop的状态改变
6.RunLoop运行逻辑
7.RunLoop实际应用
a、autoreleasepool自动释放池
在主线程的runloop里注册了两个observer,即将进入(Entry)loop时创建_objc_autoreleasePoolPush(),优先级最高;在即将休眠(BeforeWaiting)时会释放旧池并创建新池_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush(),即将退出(Exit)loop时会释放池子_objc_autoreleasePoolPop(),优先级最低。
b、更新UI
在mainloolp中即将休眠(BeforeWaiting)和即将退出(Exit)注册,会检查setNeedDisplay判断视图是否需要更新,所以更新UI需要在主线程中。
c、线程保活
NSCondition条件锁,当有任务时唤醒线程来处理。
利用runloop保活:
runloop开启方式:
d、监听优化系统卡顿
1)当runloop长时间停在kCFRunLoopBeforeSources,导致无法休眠,或者停在kCFRunLoopAfterWaiting,在唤醒后接受时间太长,就可以认为是卡顿。
2)注册观察者,并添加到主线程的commonmode中,观察runloop的所有状态,设置回调函数
3)设置信号量为0,如果<0则会阻塞当前线程,在子线程使用wait阻塞线程,并设置超时时间,超时会signal继续执行线程,或者收到主线程的消息,也会signal
4)当长时间被阻塞,查看是否在beforesources或者afterwaiting状态并且超时了,记为卡顿。