美文网首页
16 - RunLoop

16 - RunLoop

作者: RadioWaves | 来源:发表于2017-07-02 16:57 被阅读11次

1 - RunLoop - 运行循环

1 - 基本作用:

  • 保持程序的持续运行;
  • 处理App中的各种事件(比如触摸事件、定时器事件、Selector事件);
  • 节省CPU资源,提高程序性能:该做事时做事,该休息时休息.


2 - RunLoop对象

  • iOS中有2套API来访问和使用RunLoop
  • Foundation中的NSRunLoop和Core Foundation中的CFRunLoopRef
  • NSRunLoop和CFRunLoopRef都代表着RunLoop对象
  • NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构需要多研究CFRunLoopRef层面的API (Core Foundation层面)

3 - RunLoop与线程

  • 每条线程都有唯一的一个与之对应的RunLoop对象,主线程的RunLoop已经自动创建好了.
  • 子线程的RunLoop需要主动创建.
  • RunLoop在第一次获取时创建,在线程结束时销 毁.

4 - 获得RunLoop对象

#Foundation中
// 获得当前线程的RunLoop对象
[NSRunLoop currentRunLoop];                             
// 获得主线程的RunLoop对象
[NSRunLoop mainRunLoop];                                   

#Core Foundation中
// 获得当前线程的RunLoop对象
CFRunLoopGetCurrent();                                  
// 获得主线程的RunLoop对象
CFRunLoopGetMain();                                    

RunLoop处理逻辑-官方版


RunLoop的事件队列

RunLoop处理逻辑-网友整理版



2 - RunLoop相关类

由于苹果公司只暴露了RunLoop类,无聊了解的更加清楚,所以只能查看C语言的RunLoop。
C语言的RunLoop以及runtime都是开源的:http://opensource.apple.com.

Core Foundation中关于RunLoop的5个类:

  1. CFRunLoopRef --- RunLoop本身
  2. CFRunLoopModeRef --- RunLoop的运行模式
  3. CFRunLoopSourceRef --- RunLoop的事件源
  4. CFRunLoopObserverRef --- RunLoop的观察者/监听者
  5. CFRunLoopTimerRef --- RunLoop的定时器
一个RunLoop对象想要跑起来,内部必须包含Mode,而Mode内部也必须包含若干个Source/Observer/Timer,否则是无法循环的

CF的内存管理(Core Foundation)
凡是带有CreateCopyRetain等字眼的函数,创建出来的对象,都需要在最后做一次release

CFRunLoopObserverCreate
release函数:CFRelease(对象);

2.1 - CFRunLoopModeRef(运行模式)

CFRunLoopModeRef代表RunLoop的运行模式.
  • 一个 RunLoop 包含若干个 Mode

  • 每个Mode又包含若干个 Source/ Timer / Observer

  • 每次RunLoop启动时,只能指定其中一个Mode这个Mode被称作CurrentMode

  • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入

  • 这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响

  • 系统默认注册了5个Mode:
    1 - KCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个模式下运行.
    2 - UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    3 - UIInitializationRunLoopMode:在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
    4 - GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
    5 - KCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode

  • 默认情况下只需用到1, 2, 5 ;

  • 对应的Mode对应处理的事件,这样会让各自处理的事情更快,更专业!

2.2 - CFRunLoopTimerRef (定时器)

  • CFRunLoopTimerRef是基于时间的触发器

  • CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响

  • GCD的定时器不受RunLoop的Mode影响!

示例1

需求:图片中是textView控件,当正在滚动textView的时候,不让定时器,打印


解决方案:


Snip20161027_24.png
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
  • 当我将NSTimer添加到NSDefaultRunLoopMode后才会触发打印的方法。
  • 当正在滑动textView的话RunLoop的Mode会转变 UITrackingRunLoopMode,由于我们只是将定时器添加进了NSDefaultRunLoopMode,所以当RunLoop转变为UITrackingRunLoopMode的时候定时器将不在工作,
  • 这样不仅会让RunLoop专心处理界面滑动的事件,而且会让滚动的流畅度提高。

示例2

还按照上面的例子来说,如果我想textView无论是滚动还是不滚动的情况下,定时器依然有打印该怎么弄呢?
这个时候我们可以将Mode换种模式就可以了。

Snip20161028_27.png
按照图片的显示,进入头文件后可以看到两种Mode。
一种是NSDefaultRunLoopMode一种是NSRunLoopCommonModes.
我们使用第二种就可以了
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode: NSRunLoopCommonModes];

运行后,打印下currentRunLoop内部的东西,我们来看下。



从log上就可以看出当前的RunLoop内部包含了2种Mode。无论是在textView静止或者滚动的状态,textView都是会打印的。

示例3

还按照上面的例子来说,如果我想textView只在textView滚动的时候定时器才有打印该怎么弄呢?
这个时候我们可以将Mode换成UITrackingRunLoopMode就可以了、


可是这个时候我们可以看到,进入Mode的头文件是看不到UITrackingRunLoopMode,这个时候可以看到UITrackingRunLoopMode是以UI开头的,所以要进到UIKit框架才可以看到

  • 这时就可以做到只有当你滚动textView的时候才会打印log了。

关于ScheduledTimer

Snip20161028_33.png

关于往RunLoop的Mode中添加timer的方式

添加timer只有这一种方式

关于CADisplayLink


2.3 - CFRunLoopSourceRef(事件源)

CFRunLoopSourceRef一般是由系统来决定的。
CFRunLoopSourceRef一般是事件。 比如:点击事件,触摸事件...都是处于Source中。


  • 按照官方文档,Soure的分类(理论上的分类)
  • Port-Based Sources : 基于端口,和其他线程进程交互 ,通过内核发来消息.
  • Custom Input Sources : 自定义输入源 .
  • Cocoa Perform Selector Sources : 基于Perform Selector源.

  • 按照函数调用栈,Soure的分类(实践上的分类)
  • Source0:非基于Port的
  • Source1:基于Port的,通过内核和其他线程通信,接收,分发系统事件
Source1是不会在函数调用栈显示的,Source1会先接收事件,比如触摸了屏幕硬件的表面,然后通过Source1包装成Event分发给Source0来处理 查看函数调用栈

2.4 - CFRunLoopObserverRef(观察者/监听者)

  • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变


想添加Observer只能通过C语言的代码来添加。

代码实现

Warning:

由于是用C语言创建的Observer所以,
凡是带有create /copy/retain等字眼创建的函数,创建出来的对象,都需要在最后做一次release

相关文章

网友评论

      本文标题:16 - RunLoop

      本文链接:https://www.haomeiwen.com/subject/ugtocxtx.html