RunLoop

1.RunLoop目的?

1.保证程序不退出

2.负责监听事件!(触摸,时钟,网络事件)

3. 睡眠中,等待消——处理消息

2.线程跟RunLoop有什么关系?

一对一关系
1、每条线程都有唯一的一个与之对应的RunLoop对象
2、RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
3、线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建([NSRunLoop currentRunLoop])
4、RunLoop会在线程结束时销毁
5、主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

3.RunLoop的组成

 

 

Runloop中可以存在多个mode,mode是一种运行场景。

Runloop一次只能在一种mode下运行,切换mode必须停止后重新开始运行。

默认Runloop至少带有

  • Default mode(默认模式,大部分操作均在此模式)

  • Event tracking mode(追踪用户拖动或者其他交互时使用)

  • Common modes(是一个模式集合,runloop会把加入commonItems的事件同步到common modes中的所有mode,默认集合中已有default mode和event tracking mode)
    可以自己为runloop添加自定义mode。

每个mode封装了自己需要响应的事件和需要通知的observer。这些事件的形式有两种:timer和input source。另外可以在mode中注册observer来观察runloop的运行状态。

  • timer:给线程发送同步事件,但是是在未来指定时间发送或者周期性的响应事件。

  • source:给线程发送异步事件,分为两种,source0和source1。source0需要手动在另一个线程手动触发(通过调用 void CFRunLoopSourceSignal ( CFRunLoopSourceRef source ); ),CFSocket就是这种形式;source1是基于mach port的事件,程序只要把message塞给port,底层就会触发runloop中对应source1。目前CFMachPort和CFMessagePort是这种形式。以上描述来自CFRunloopSource Reference

  • observer:可以观察六种runloop的运行状态:

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
     kCFRunLoopEntry = (1UL << 0),//进入runloop
     kCFRunLoopBeforeTimers = (1UL << 1),//准备处理timer
     kCFRunLoopBeforeSources = (1UL << 2),//准备处理source
     kCFRunLoopBeforeWaiting = (1UL << 5),//准备休眠
     kCFRunLoopAfterWaiting = (1UL << 6),//休眠结束,处理事件之前
     kCFRunLoopExit = (1UL << 7),//runloop退出
     kCFRunLoopAllActivities = 0x0FFFFFFFU//所有状态
};

这里要提一句的是,timer和source1(也就是基于port的source)可以反复使用,比如timer设置为repeat,port可以持续接收消息,而source0在一次触发后就会被runloop移除。

4.如何启动RunLoop

OSX/iOS 系统中,提供了两个这样的对象:NSRunLoop 和 CFRunLoopRef。
CFRunLoopRef 是在 CoreFoundation 框架内的,它提供了纯 C 函数的 API,所有这些 API 都是线程安全的。
NSRunLoop 是基于 CFRunLoopRef 的封装,提供了面向对象的 API,但是这些 API 不是线程安全的。

[NSRunLoop currentRunLoop];

5.程序中添加每3秒响应一次的NSTimer,当拖动tableview时timer可能无法响应要怎么解决?

runloop 常用的mode有两种 :NSDefaultRunLoopMode、UITrackingRunLoopMode
程序默认处于 NSDefaultRunLoopMode 模式下  但是页面滑动的时候 runloop的模式就会切换到 UITrackingRunLoopMode 此时就会退出 NSDefaultRunLoopMode ,在NSDefaultRunLoopMode模式下所有的操作都会被停止(如:NSTimer事件响应)。当页面停止滑动时 就会自动切换到NSDefaultRunLoopMode,(此时NSTimer才会继续工作 )

因此:想要在当拖动tableview时timer继续工作 就必须把 timer 添加到runloop任何模式都能工作。代码如下
 [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

NSRunLoopCommonModes:并不是一个真的模式,它只是一个标记

6.讲讲 RunLoop,项目中有用到吗?

1、控制线程生命周期(线程保活)
2、解决NSTimer在滑动时停止工作的问题
3、监控应用卡顿
4、性能优化

7.RunLoop的运行逻辑

 

 

Source0 作用
1、触摸事件处理
2、performSelector:onThread:

Source1 作用
1、基于Port的线程间通信
2、系统事件捕捉

Timers 作用
1、NSTimer
2、performSelector:withObject:afterDelay:

Observers 作用
1、用于监听RunLoop的状态 

根据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

 

posted @ 2022-03-30 10:41  朝阳向日葵  阅读(137)  评论(0编辑  收藏  举报