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个类:
- CFRunLoopRef --- RunLoop本身
- CFRunLoopModeRef --- RunLoop的运行模式
- CFRunLoopSourceRef --- RunLoop的事件源
- CFRunLoopObserverRef --- RunLoop的观察者/监听者
- CFRunLoopTimerRef --- RunLoop的定时器

CF的内存管理(Core Foundation)
凡是带有Create
、Copy
、Retain
等字眼的函数,创建出来的对象,都需要在最后做一次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的时候,不让定时器,打印

解决方案:

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换种模式就可以了。

按照图片的显示,进入头文件后可以看到两种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

关于往RunLoop的Mode中添加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的,通过内核和其他线程通信,接收,分发系统事件


2.4 - CFRunLoopObserverRef(观察者/监听者)
-
CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
想添加Observer只能通过C语言的代码来添加。
代码实现


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