RunLoop

概念

一个非主线程的声明周期,是从线程刚开始创建到任务结束的,但是假如我们想让一个线程一直处于待处理状态,即随时可以接受外界调用,则需要用到RunLoop。即RunLoop是一个对象,管理其需要处理的事件和消息,并提供了一个入口函数执行一个循环,使线程一直处于这个循环中,直到外界传入了结束消息。

与线程的关系

RunLoop 与线程是一一对应的。关系保存在一个全局字典里面,线程刚创建的时候,RunLoop 并没有创建,而是在第一次调用的时候才被创建,在线程结束的时候销毁。只能在线程内部调用它的RunLoop(主线程除外)

相关类

CoreFoundation 框架中,有5个关于 RunLoop 的类:
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

框架中并没有暴露 CFRunLoopModeRef 的接口,而是通过 CFRunLoopRef 进行了封装。他们之间的关系如下:
这里写图片描述
一个 RunLoop 包括多个 Mode ,而一个 Mode 包含多个 Source 、Observer 、Timer 。一个 RunLoop 只能以一个 Mode 运行,切换 Mode 的话,必须退出 RunLoop ,再重新指定一个 Mode 进入。

CFRunLoopSourceRef

Source 是产生事件的地方,有两个版本。
1.Source 0 ,只包含一个函数指针,不能自动调用这个指针,而是手动调用 CFunLoopSourceSignal(source) 将这个 source 标记为待处理状态,然后调用 CFRunLoopWakeUp(runloop) 将 runloop 唤醒,让其处理这个 source
2.Source 1 ,包含一个 mach_port 和一个回调(函数指针),port 即接口,用于内核和其他线程互相发送消息,后面会详细讲解他的用法。

CFRunLoopTimerRef

基于时间的触发器,包含一个时间长度和一个回调(函数指针),当时间点到达时,RunLoop 自动调用回调函数。

CFRunLoopObserverRef

观察者对象,可以观察 RunLoop 的各个状态,即每个观察者都有一个回调指针,当 RunLoop 状态发生变化时,观察者被调用。RunLoop 包括以下几种状态:

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry         = (1UL << 0), // 即将进入Loop
    kCFRunLoopBeforeTimers  = (1UL << 1), // 即将处理 Timer
    kCFRunLoopBeforeSources = (1UL << 2), // 即将处理 Source
    kCFRunLoopBeforeWaiting = (1UL << 5), // 即将进入休眠
    kCFRunLoopAfterWaiting  = (1UL << 6), // 刚从休眠中唤醒
    kCFRunLoopExit          = (1UL << 7), // 即将退出Loop
};

CFRunLoopModeRef

struct __CFRunLoopMode {
    CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;    // Set
    CFMutableSetRef _sources1;    // Set
    CFMutableArrayRef _observers; // Array
    CFMutableArrayRef _timers;    // Array
    ...
};

struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set
    CFMutableSetRef _commonModeItems; // Set
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set
    ...
};

RunLoopMode 与 RunLoop 的结构大致如上。
RunLoop 结构体中,有个 _commonModes 概念,Mode 可以把自己设置为 Common 状态(通过将自己的 ModeName 加入到 _commonModes 中,这样每次 RunLoop 的内容发生变化时, RunLoop 都会讲 _commonModeItems 中的 item 加入到 _commonModes 中的每个 Mode 中。

RunLoop 实现的功能

AutoReleasePool

自动释放池,假如将一段代码加入到自动释放池,那么里面使用到的对象,究竟是在什么时候被释放掉呢。
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其 order 是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。

第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池。这个 Observer 的 order 是 2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。这些回调会被 RunLoop 创建好的 AutoreleasePool 环绕着,所以不会出现内存泄漏,开发者也不必显示创建 Pool 了。

事件响应

苹果注册了一个 Source1 (基于 mach port 的) 用来接收系统事件,其回调函数为 __IOHIDEventSystemClientQueueCallback()。

当一个硬件事件(触摸/锁屏/摇晃等)发生后,首先由 IOKit.framework 生成一个 IOHIDEvent 事件并由 SpringBoard 接收。这个过程的详细情况可以参考这里。SpringBoard 只接收按键(锁屏/静音等),触摸,加速,接近传感器等几种 Event,随后用 mach port 转发给需要的App进程。随后苹果注册的那个 Source1 就会触发回调,并调用 _UIApplicationHandleEventQueue() 进行应用内部的分发。

_UIApplicationHandleEventQueue() 会把 IOHIDEvent 处理并包装成 UIEvent 进行处理或分发,其中包括识别 UIGesture/处理屏幕旋转/发送给 UIWindow 等。通常事件比如 UIButton 点击、touchesBegin/Move/End/Cancel 事件都是在这个回调中完成的。

手势响应

当上面的 _UIApplicationHandleEventQueue() 识别了一个手势时,其首先会调用 Cancel 将当前的 touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。

苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件,这个Observer的回调函数是 _UIGestureRecognizerUpdateObserver(),其内部会获取所有刚被标记为待处理的 GestureRecognizer,并执行GestureRecognizer的回调。

当有 UIGestureRecognizer 的变化(创建/销毁/状态改变)时,这个回调都会进行相应处理。

界面更新

当在操作 UI 时,比如改变了 Frame、更新了 UIView/CALayer 的层次时,或者手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理,并被提交到一个全局的容器去。

苹果注册了一个 Observer 监听 BeforeWaiting(即将进入休眠) 和 Exit (即将退出Loop) 事件,回调去执行一个很长的函数:

_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()。这个函数里会遍历所有待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。

这个函数内部的调用栈大概是这样的:

_ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()
    QuartzCore:CA::Transaction::observer_callback:
        CA::Transaction::commit();
            CA::Context::commit_transaction();
                CA::Layer::layout_and_display_if_needed();
                    CA::Layer::layout_if_needed();
                        [CALayer layoutSublayers];
                            [UIView layoutSubviews];
                    CA::Layer::display_if_needed();
                        [CALayer display];
                            [UIView drawRect];

定时器

使用了 timer 的方式实现

PerformSelecter

网络请求

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值