ios runloop

runloop相关知识
runloop 即 运行循环,可理解为无限循环,优点是能够在闲暇时间等待,有事件则进行分发处理,对资源的占用起到了节源的作用。内部使用do while 循环实现。

RunLoop 相关五个类:
CFRunLoopRef:RunLoop对象
CFRunLoopModeRef:循环模式
CFRunLoopSourceRef:输入源/事件源 分为source0和source1
CFRunLoopTimerRef:定时源
CFRunLoopObserverRef:观察者

runloop可以处理几种事件源:
1、source0:触摸事件等(非基于 Port。只包含了一个回调,不能主动触发事件。使用时,需先调用 CFRunLoopSourceSignal(source),将 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop)唤醒 RunLoop,让其处理这个事件)

2、source1:系统内部事件(基于Port,通过内核和
其他线程通信,接收、分发系统事件。 包含了一个 mach_port 和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程)

3、Timer:计时器

4、Observe:监听runloop的状态
CFRunLoopObserverRef是观察者,主要用来监听RunLoop 的状态,主要有以下几种状态。
kCFRunLoopEntry : 即将进入RunLoop
kCFRunLoopBeforeTimers :即将处理Timer
kCFRunLoopBeforeSources:即将处理Source
kCFRunLoopBeforeWaiting :即将进入休眠
kCFRunLoopAfterWaiting:即将从休眠中唤醒
kCFRunLoopExit :即将从RunLoop中退出
kCFRunLoopAllActivities:监听全部状态改变

runloop 几种 Mode
1、kCFRunLoopDefaultMode : 默认模式,主线程是在这个运行模式下运行

2、UITrackingRunLoopMode : 跟踪用户交互事件(用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响)

3、UIInitializationRunLoopMode : 在刚启动App时第进入的第一个 Mode,启动完成后就不再使用

4、GSEventReceiveRunLoopMode : 接受系统内部事件,通常用不到

5、kCFRunLoopCommonModes : 伪模式,不是一种真正的运行模式,实际是kCFRunLoopDefaultMode 和 UITrackingRunLoopMode的结合。

对于函数运行在哪个输入源 ,我们可以使用断点然后使用 bt(使用方法同 po相同) 进行 堆栈信息输出

runloop 与线程
我们都知道程序启动入口是main,那么main返回了一个函数,

UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));

这个函数其实起到了一个启动 mainRunloop的作用,程序启动在主线程,我们需要主线程一直保持存在状态,所以需要runloop来支撑,这个runloop就是mainRunLoop,如果没有mainRunLoop程序会立马退出。
每一个线程最多只能有一个runloop,如果我们需要在子线程运行runloop,需要我们手动启动runloop,[NSRunLoop currentRunLoop] 就可以获取当前runloop,如果runloop存在,则直接拿到,不存在,系统会创建runloop,可以理解为懒加载方式。[[NSRunLoop currentRunLoop] run]; 即启动了runloop。

实际开发中,我们可能用到的地方:
1、线程常驻(案例:AFNetworking )
2、滚动视图和 timer 的使用,滚动视图在滚动状态下,timer停止,将timer加入到commonMode即可。
3、AutoreleasePool
应用程序一旦启动,主线程 RunLoop 里注册了两个 Observer。一个 Observer 监听即将进入 Loop 事件,回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池,并保证创建释放池发生在其他所有回调之前。另外一个 Observer 监视了两个事件 (RunLoop即将进入休眠和即将退出 RunLoop 事件) ,前者会调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;后者会调用 _objc_autoreleasePoolPop() 来释放自动释放池,并保证释放自动释放池事件发生在其它回调之后。
4、卡顿监测
所谓的卡顿一般是在主线程做了耗时操作,卡顿监测的主要原理是在主线程的 RunLoop 中添加一个 observer,检测从 即将处理Source(kCFRunLoopBeforeSources) 到 即将进入休眠 (kCFRunLoopBeforeWaiting) 花费的时间是否过长。如果花费的时间大于某一个阙值,则认为卡顿,此时可以输出对应的堆栈调用信息。
参考文章

你可能感兴趣的:(ios runloop)