runloop学习记录

作者: Corbin___ | 来源:发表于2018-04-07 15:40 被阅读12次

1.runloop源码

源码

Snip20180407_10.png

2.对象介绍

image.png

接下来的对象介绍,可以借助这个图理清对象间的关系

// 一个runloop对象
typedef struct __CFRunLoop * CFRunLoopRef;
// source
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;
// observer观察者
typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;
// timer
typedef struct __CFRunLoopTimer * CFRunLoopTimerRef;
// mode
typedef struct __CFRunLoopMode *CFRunLoopModeRef;

2.1. CFRunLoopRef

一个指向__CFRunLoop结构体的指针

struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;          /* locked for accessing mode list */
    __CFPort _wakeUpPort;           // used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFTypeRef _counterpart;
};

从这个结构体的内容可以看出:

  • 一个runloop对象主要包含modes、线程
  • modes是一个可变集合,里面存着mode,所以一个runloop对象可以包含多个mode
  • 一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。(这三个的概念后面讲)。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。

2.2. CFRunLoopSourceRef

一个指向__CFRunLoopSource结构体的指针

struct __CFRunLoopSource {
    CFRuntimeBase _base;
    uint32_t _bits;
    pthread_mutex_t _lock;
    CFIndex _order;         /* immutable */
    CFMutableBagRef _runLoops;
    union {
    CFRunLoopSourceContext version0;    /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1;   /* immutable, except invalidation */
    } _context;
};
  • 结构体有一个_runLoops(可变包对象),说明一个source可以添加到多个runloop中

  • CFRunLoopSourceContext version0

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
    void    (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*perform)(void *info);
} CFRunLoopSourceContext;

  • CFRunLoopSourceContext1 version1
typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
    mach_port_t (*getPort)(void *info);
    void *  (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
    void *  (*getPort)(void *info);
    void    (*perform)(void *info);
#endif
} CFRunLoopSourceContext1;
  • version0和version1区别
    当我们接受到的消息(source),比如触摸事件,滑动事件等等;这个source是有两种的,一种是version0,也就是用source0来表示,一种是version1 -> source1
    区别
version0 / source0 source0 是非基于 port 的事件,主要是 APP 内部事件,如点击事件,触摸事件等
version1 / source1 source1 是基于Port的,通过内核和其他线程通信,接收,分发系统事件。

2.3. CFRunLoopObserverRef

一个指向__CFRunLoopObserver结构体的指针
作用:观察者,观察runloop的各种状态,并通过回调抛出去

struct __CFRunLoopObserver {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFIndex _rlCount;
    CFOptionFlags _activities;      /* immutable */
    CFIndex _order;         /* immutable */
    CFRunLoopObserverCallBack _callout; /* immutable */
    CFRunLoopObserverContext _context;  /* immutable, except invalidation */
};

其中的CFOptionFlags是一个枚举:表示runloop的状态

/* Run Loop Observer Activities */
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
    kCFRunLoopAllActivities = 0x0FFFFFFFU    // 占位
};


CFRunLoopObserverRef观察者会将观察到的状态变化通过回调_callout跑出去
看下这个CFRunLoopObserverCallBack _callout

// 这个回调可以将观察者、runloop状态、info传出去
typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);

2.4. CFRunLoopTimerRef

是一个指向__CFRunLoopTimer结构体的指针

某些数据类型能够在Core Foundation和Foundation之间互换使用,可被互换使用的数据类型被称为Toll-Free Bridged类型。

  • CFRunLoopTimerRef 是定时器,可以在设定的时间点抛出回调
  • CFRunLoopTimerRef和NSTimer是toll-free bridged的,可以相互转换。
struct __CFRunLoopTimer {
    CFRuntimeBase _base;
    uint16_t _bits;
    pthread_mutex_t _lock;
    CFRunLoopRef _runLoop;
    CFMutableSetRef _rlModes;
    CFAbsoluteTime _nextFireDate;
    CFTimeInterval _interval;       /* immutable */
    CFTimeInterval _tolerance;          /* mutable */
    uint64_t _fireTSR;          /* TSR units */
    CFIndex _order;         /* immutable */
    CFRunLoopTimerCallBack _callout;    /* immutable */
    CFRunLoopTimerContext _context; /* immutable, except invalidation */
};

2.5.CFRunLoopModeRef

一个指向__CFRunLoopMode结构体的指针

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR */
};

  • _sources0、 _sources1都是可变集合对象,对应着用来存取CFRunLoopSourceRef对象,CFRunLoopSourceRef对象中有version0version1分别对应着_sources0_sources1
  • 包含了observer、timer
  • CFStringRef _name就是mode的名字,如:kCFRunLoopDefaultMode
  • 有几种mode图片来源
    image.png

3.函数介绍

3.1.__CFRunLoopDoObservers

通知Observer,runloop要做什么事情
这个__CFRunLoopDoObservers函数需要传三个参数,分别是

  • CFRunLoopRef(runloop对象)
  • CFRunLoopModeRef(runloop的mode)
  • CFRunLoopActivity(runloop的状态枚举)


    这个函数的实现

3.2._CFRunLoopGet0

runloop对象是存在全局字典中的,key就是pthread_t
这个_CFRunLoopGet0函数的作用就是获取对应线程的runloop

实现思路(依据就是下面截图的源码)
1.先判断这个全局字典存不存在,不存在,创建一个,并将主线程的runloop加进去
2.直接去字典里取这个loop
3.如果loop不存在,就创建一个loop加入到全局字典中
// 伪代码
if(!__CFRunLoops) {
      1.创建全局字典
      2.创建主线程loop,并加入到全局字典中
}
根据线程pthread_t为key,去字典取对应的loop
if(!loop) {
      1.创建loop
      2.加入到字典中
}
return loop

其实这个说明了runloop和线程是一一对应的关系

Snip20180407_22.png

3.3.获取主线程loop和获取当前的loop

image.png

参考文档

强力推荐
RunLoop系列之要点提炼
RunLoop系列之源码分析
iOS刨根问底-深入理解RunLoop
深入理解 RunLoop

相关文章

  • RunLoop学习总结

    通过以下文章学习记录 关于Runloop的原理探究及基本使用 深入理解RunLoop RunLoop完全指南 Ru...

  • runloop学习记录

    1.runloop源码 源码 2.对象介绍 接下来的对象介绍,可以借助这个图理清对象间的关系 2.1. CFRun...

  • RunLoop详解

    写在前面 本文仅是自己学习RunLoop的一个记录,参考了ibireme大神的 深入理解RunLoop[https...

  • RunLoop实现学习记录

    注:以Core Foundation的实现作为参考(版本为CF-855.17)。 1. 相关数据结构 1.1 ru...

  • IOS runloop 学习笔记

    这次学习 的内容是 runloop 1.runloop 是什么2.runloop 的作用3.runloop 和 线...

  • RunLoop 记录

    深入理解RunLoop

  • Runloop记录

    流程 添加 Observer 可以监听到 RunLoop 的各种状态 kCFRunLoopEntry: 进入 Ru...

  • RunLoop学习资料

    非常好的runloop学习系列 CoreFoundation源码 RunLoop系列之源码分析 关于Runloop...

  • iOS RunLoop理解

    在网上看到一篇对RunLoop讲述挺好的文章,在此记录一下深入理解RunLoop

  • NSRunLoop

    前言 RunLoop的初期学习总结,后续会持续研究更新。 一、Runloop定义及作用 1. 什么是Runloop...

网友评论

    本文标题:runloop学习记录

    本文链接:https://www.haomeiwen.com/subject/gbsehftx.html