iOS中的RunLoop

来源:互联网 发布:linux date 北京时间 编辑:程序博客网 时间:2024/06/04 00:55

iOS中的RunLoop

什么是RunLoop

从字面上理解就是一个运行循环,我们一般程序就是执行一个线程,是一条直线.有起点终点.而runloop就是一直在线程上面画圆圈,一直在跑圈,在不断跑圈中,一直在检测一些点击事件、定时器等等,一旦检测到就开始执行,执行结束后再睡眠,睡眠中再检测,除非切断否则一直在运行,否则就一直在循环。其内部的结构是一个do-while循环,在这个循环内部不断处理各种任务(比如timer、source、Observer)

RunLoop基本作用

  • 保持程序的持续运行,App持续运行就是因为有RunLoop
  • 处理App中的各种事件(比如触摸事件、定时事件、Selector事件)
  • 节省CPU资源,提高程序性能,该做事时做事,该休息时休息

RunLoop对象

  • Foundation
    NSRunLoop
  • Core Foundation
    CFRunLoopRef

    NSRunLoop和CFRunLoopRef都代表着RunLoop对象,NSRunLoop是基于CFRunLoopRef的一层OC的包装

RunLoop与线程
  • 每条线程都有唯一的一个与之相对应的RunLoop对象
  • 主线程的RunLoop系统已经创建好了,子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁

      // 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef  static CFMutableDictionaryRef loopsDic; // 访问 loopsDic 时的锁 static CFSpinLock_t loopsLock;// 获取一个 pthread 对应的 RunLoop。CFRunLoopRef _CFRunLoopGet(pthread_t thread) {    OSSpinLockLock(&loopsLock);    if (!loopsDic) {         // 第一次进入时,初始化全局Dic,并先为主线程创建一个 RunLoop。        loopsDic = CFDictionaryCreateMutable();        CFRunLoopRef mainLoop = _CFRunLoopCreate();        CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);    }    // 直接从 Dictionary 里获取。    CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));    if (!loop) {        // 取不到时,创建一个        loop = _CFRunLoopCreate();        CFDictionarySetValue(loopsDic, thread, loop);        // 注册一个回调,当线程销毁时,顺便也销毁其对应的 RunLoop。        _CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);    }    OSSpinLockUnLock(&loopsLock);    return loop;}CFRunLoopRef CFRunLoopGetMain() {    return _CFRunLoopGet(pthread_main_thread_np());}CFRunLoopRef CFRunLoopGetCurrent() {    return _CFRunLoopGet(pthread_self());}

    从上面的代码可以看出,线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。

    //获取当前线程的RunLoop[NSRunLoop currentRunLoop]; //获取主线程的RunLoop[NSRunLoop mainRunLoop];//获取当前线程的RunLoopCFRunLoopGetMain();//获取主线程的RunLoopCFRunLoopGetCurrent();

    注:NSRunLoop初始化不需要alloc,只需要在该线程里调用[NSRunLoop currentRunLoop],

RunLoop与相关类

(主要针对Core Foundation中的RunLoop的5个类)
  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

    其中 CFRunLoopModeRef 类并没有对外暴露,只是通过 CFRunLoopRef 的接口进行了封装。他们的关系如下:
    这里写图片描述

CFRunLoopModeRef

  • CFRunLoopModeRef代表RunLoop得运行模式
  • 一个RunLoop包含了若干个Mode,每个Mode又包含了若干个Source/Timer/Observer
  • 每次RunLoop启动时,只能指定其中一个Mode,这个Mode被称作CurrentMode
  • 如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入循环
  • 每个Mode苦于设置自己的 Source/Timer/Observer,让其互不影响

  • 系统默认注册了5个Mode(苹果只开放了前俩个)

    • kCFRunLoopDefaultMode:App的默认Mode,通常主线程是这个Mode下运行
    • UITrackingRunLoopMode:界面跟踪Mode,用于ScrollerView追踪触摸滑动,保证界面滑动时不受其他Mode影响
    • UIInitalizaationRunLoopMode:在刚启动App时进入的第一个Mode,启动完成后就不再使用
    • GSEventReceiveRunLoopMode:接受系统事件内部的Mode,通常用不到
    • kCFRunLoopCommonModes:这是占位的Mode,不是真正的Mode

CFRunLoopTimerRef

  • CFRunLoopTimerRef是一个基于时间触发器

CFRunLoopSourceRef

  • CFRunLoopSourceRef是事件源(输入源)
  • 按照官方文档,Source的理论分类
    • Port-Based Source
    • Custom Input Source
    • Cocoa Perform Selector Sources
  • 按照函数调用栈,Source的实践分类
    • Source0:非基于Prot的
    • Source1:基于Prot的,通过内核和其他线程通信,接受、分发系统事件

CFRunLoopObserverRef

  • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变
  • 可以监听的时间点有以下几个

    typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {    kCFRunLoopEntry = (1UL << 0),//即将进入Runloop    kCFRunLoopBeforeTimers = (1UL << 1),//即将处理Timer    kCFRunLoopBeforeSources = (1UL << 2),//即将处理Source事件    kCFRunLoopBeforeWaiting = (1UL << 5),//即将进入休眠    kCFRunLoopAfterWaiting = (1UL << 6),//从刚才休眠中唤醒    kCFRunLoopExit = (1UL << 7),//即将退出runloop    kCFRunLoopAllActivities = 0x0FFFFFFFU//监听所有状态};
  • * CF的内存管理*

    • 1、凡是带有Create、copy、retain等字眼的函数,创建出来的对象,都需要在最后做一次release
    • 2、release函数:CFRelease(observer);

RunLoop处理逻辑

这里写图片描述

RunLoop在开发中的使用场景

1、开启一个常驻线程(让一个子线程不进入消亡状态,等待其他线程发来消息,处理事件)

  • 在子线程中开启一个定时器
  • 在子线程中进行长期监控一个行为事件

2、可以控制定时器在特定模式下执行

3、可以让某些事件在特定模式下执行

4、可以添加Observer监听RunLoop的状态,比如监听点击事件的处理(在所有点击事件之前做一些事情)

最后,附上以上的demo,git:(https://github.com/hejiasu/RunLoop)

1 0