iOS中的Runloop(原创)
2015-08-31 本文已影响526人
阿诺的小弟
1.什么是Runloop
从字面上理解,Runloop指的就是运行循环,iOS中,只要程序启动, 就会创建Runloop,用来处理各种事件(比如定时器事件, 触摸事件等).
2.Runloop有什么用
它的主要作用有以下几个:
1.保持程序的运行:如果没有它, 程序一启动就over了;
2.事件的处理: 包括按钮点击事件, 屏幕的点击事件等;
3.定时器处理: NSTimer的处理;
4.节约CPU, 提高性能:有任务的时候干活,没有任务时候休息;
3.iOS中Runloop是如何实现的
在iOS中, 程序的入口仍然是main函数:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
可以看到, UIApplicationMain函数内部就启动了一个Runloop, 而且UIApplicationMain函数一直没有返回, 这保证了程序的持续运行, 这个Runloop称为主运行循环, 是与主线程相关联的.
实际上, UIApplicationMain函数里面有一个do-while循环, 保证了Runloop不死.
4.iOS中Runloop如何访问
在iOS中, 可以通过两套API来访问Runloop:
1.Foundation框架:
OC中的框架, 直接访问NSRunLoop对象.
2.Core Foundation框架:
c语言的框架, 通过CFRunLoopRef访问.
实际上, NSRunLoop和CFRunLoopRef是同一个对象, 只不过通过不同的API来访问的,NSRunLoop是对CFRunLoopRef的封装.
5.Runloop与线程的关系
iOS中, 一条线程就对应一个Runloop, 为啥这么说呢, 从官方文档中, 可以看出, Runloop和线程是以字典的形式进行匹配的, key是线程, value是Runloop对象, 这样就保证了一条线程对应一个Runloop了.(别急, 后面会附带官方文档地址的.)
而且,主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建.
6.Runloop内部包含什么内容
这个问题, 首先从如何获取Runloop对象说起, OC中, 使用下面代码就可以获得Runloop对象:
// Foundation
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
// Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
我打印了这个Runloop对象, 里面的内容如下:(由于内容比较多, 只将重点内容列出)
// runloop对象
<CFRunLoop 0x7fb13172abc0 [0x10a5aa180]>{wakeup port = 0x1003, stopped = false, ignoreWakeUps = true,
// 当前模式
current mode = UIInitializationRunLoopMode,
common modes = <CFBasicHash 0x7fb13172ac80
// common modes标记了哪两个模式(后面会讲为啥是标记?)
0 : <CFString 0x10b27ce50 [0x10a5aa180]>{contents = "UITrackingRunLoopMode"}
2 : <CFString 0x10a586080 [0x10a5aa180]>{contents = "kCFRunLoopDefaultMode"}
}
// source: 源
6 : <CFRunLoopSource 0x7fb131415300 [0x10a5aa180]>
// observers: 观察者
observers = <CFArray 0x7fb13141d300 [0x10a5aa180]>
// timer: 定时器
timers = (null),
// 两种不同的模式
3 : <CFRunLoopMode 0x7fb13172dbb0 [0x10a5aa180]>{name = GSEventReceiveRunLoopMode, port set = 0x2003, timer port = 0x2103,
sources0 = <CFBasicHash 0x7fb13172d260 [0x10a5aa180]>{type = mutable set, count = 1,
entries =>
0 : <CFRunLoopSource 0x7fb13172da30 [0x10a5aa180]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = PurpleEventSignalCallback (0x10d8236c8)}}
}
要想知道上面内容是什么意思, 先看下面的内容:
7. Runloop中相关的类
Core Foundation中关于RunLoop的5个类
1.CFRunLoopRef
2.CFRunLoopModeRef
3.CFRunLoopSourceRef
4.CFRunLoopTimerRef
5.CFRunLoopObserverRef
可以这理解为, 一个Runloop对象里面, 必不可少的是CFRunLoopModeRef, Mode里面可以包含timer/source/oberver(只有包含了其中一个, Runloop才能在启动后保证不死).
层级结构如下图:
屏幕快照 2015-08-31 上午10.06.14.png接下来, 对里面的内容说明一下:
一.CFRunLoopModeRef:代表RunLoop的运行模式
1.一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer
2.每次RunLoop启动时,只能指定其中一个 Mode,这个Mode被称作CurrentMode
3.这样做主要是为了分隔开不同组的Source/Timer/Observer,让其互不影响
系统默认注册了5个Mode:
1.kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
2.UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
3.UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
4.GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
5.kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode, 被标记为kCFRunLoopCommonModes格式的都可以成为commonMode, 其中包含1.2两种模式.
二.CFRunLoopSourceRef:是事件源(输入源)
现在主要分为两种:
Source0:非基于Port的
Source1:基于Port的(如GCD底层实现也是通过Port的, 主要用于线程之间的通信)
三.CFRunLoopTimerRef:是基于时间的触发器, 基本上说的就是NSTimer
四.CFRunLoopObserverRef:观察者, 可以用来监听Runloop的状态, 下面列出了Runloop的状态
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即将进入
kCFRunLoopBeforeTimers = (1UL << 1), // 即将处理timer
kCFRunLoopBeforeSources = (1UL << 2), // 即将处理事件源
kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7), // 即将退出Runloop
};
下面两张图片描述了Runloop的运行和状态:
屏幕快照 2015-08-31 上午10.20.54.png 屏幕快照 2015-08-31 上午10.21.19.png
8.Runloop主要怎么运用
由于时间关系, 这个问题下次会详细讲清楚.
参考文献:
1.苹果官方文档:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
2.CFRunLoopRef开源文档:
http://opensource.apple.com/source/CF/CF-1151.16/