大话iOS之RunLoop
iOS系统中有两个关于Runloop的对象:NSRunLoop 和 CFRunLoopRef
NSRunloop是Foundation框架提供的,是对CoreFoundation框架提供的CFRunloopRef的封装。
CoreFoundation提供的是纯C语言的API,都是线程安全的,Foundation不是线程安全的。
iOS中RunLoop是开源的,在CoreFoundation的开源代码中,可以在这里下载:RunLoop源码
CFRunLoopMode
RunLoop在同一时段内只能并且必须在一种特定的Mode下run,这个Mode被称为currentMode
如果想更换当前的mode,需要暂停当前的loop,然后重启新的loop,这样可以分离开不同的Source/Timer/Observer,互不影响
可以定义自己的mode,但是几乎没有这么做的
NSDefaultRunLoopMode
默认的状态,空闲时候的状态
-
UITrackingRunLoopMode
滑动跟踪,滑动scrollView的时候处于这个状态,保证界面滑动时不受其他界面的影响 -
UIInitializationRunLoopMode
私有的mode,App启动的时候的状态,加载出第一个页面后,就转成了Default -
NSRunLoopCommonModes
默认的是包括Default
和Tracking
两个状态,也可以自定义的往里面添加 -
GSEventReceiveRunLoopMode
接受系统事件的内部 Mode,通常用不到
来看一下CFRunLoopMode的构成
RunLoopMode的结构.png RunLoop源码中的mode的定义.png可以看到,一个Runloop可以包含如果个Mode,每个Mode又包含了多个Source/Timer/Observer
CFRunLoopSource
CFRunLoopSource
是RunLoop的数据源抽象类,类似Objective-C中的协议protocol,实现了这个protocol就可以充当RunLoop的数据源(几乎没有这么做的),RunLoop自己定义了两个Source:Source0和Source1
- Source0 : 处理App内部事件,比如屏幕响应UIEvent, CFSocket,我们点击屏幕就是Source0事件
- Source1 : 由内核管理,比如mach_port
注意
:mach_port是iOS系统中进程间通信的一种方式,如果进程1往一个port中发送一个消息,此时进程2监听了这个port,就会拿到这个消息
NSPort是对CoreFoundation中的CFMachPort和CFMessagePort的封装
来看一下对source事件的定义
CFRunLoopSource
中用union确保这个source要么是source0,要么是source1看一下source0和source1的具体定义: source0的定义 source1的定义.png
souce0中定义的都是函数指针
source1中出了函数指针,还有一个mach_port
CFRunLoopObserver
CFRunLoopObserver
相当于观察者模式的观察者,用来向观察者报告RunLoop当前的状态
下面是RunLoop中定义的状态:
RunLoop所有的状态.png我们可以用下面的代码创建一个CFRunLoopObserver,添加到当前线程的RunLoop中,监听当前线程Runloop的当前状态
例如应用启动的时候,会处理一系列的source事件和timer事件,当处理完之后,就会打印出
即将进入 休眠
,此时,我们点击屏幕,会打印出刚从休眠中唤醒
,然后再处理一系列的source和timer事件,然后再次进入休眠状态。
CFRunLoopObserverRef observe = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即将进入 loop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理 timer");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理 source");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入 休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"刚从休眠中唤醒");
break;
case kCFRunLoopExit:
NSLog(@"即将退出 loop");
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observe, kCFRunLoopDefaultMode);
CFRelease(observe);
CFRunLoopObserver 与 AutoreleasePool
AutoreleasePool在RunLoop的两次sleep之间对AutoreleasePool进行pop和push,将这次loop中产生的Autorelease对象进行释放
RunLoop的运行机制
关于运行机制,先来看两幅图:
官方文档中的RunLoop的运行图.png 更容易理解的RunLoop的运行图.png上面的两张图的运行循环的逻辑,都对应着源码中的__CFRunLoopRun这个函数
RunLoop运行循环函数