大话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 默认的是包括DefaultTracking两个状态,也可以自定义的往里面添加
  • GSEventReceiveRunLoopMode接受系统事件的内部 Mode,通常用不到

来看一下CFRunLoopMode的构成

大话iOS之RunLoop_第1张图片
RunLoopMode的结构.png
大话iOS之RunLoop_第2张图片
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事件的定义

大话iOS之RunLoop_第3张图片
RunLoop中对于source事件的定义.png

CFRunLoopSource中用union确保这个source要么是source0,要么是source1
看一下source0和source1的具体定义:

大话iOS之RunLoop_第4张图片
source0的定义
大话iOS之RunLoop_第5张图片
source1的定义.png

souce0中定义的都是函数指针
source1中出了函数指针,还有一个mach_port

CFRunLoopObserver

CFRunLoopObserver相当于观察者模式的观察者,用来向观察者报告RunLoop当前的状态

下面是RunLoop中定义的状态:

大话iOS之RunLoop_第6张图片
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的运行机制

关于运行机制,先来看两幅图:

大话iOS之RunLoop_第7张图片
官方文档中的RunLoop的运行图.png
大话iOS之RunLoop_第8张图片
更容易理解的RunLoop的运行图.png

上面的两张图的运行循环的逻辑,都对应着源码中的__CFRunLoopRun这个函数


RunLoop运行循环函数

你可能感兴趣的:(大话iOS之RunLoop)