初识Runloop

263 阅读4分钟

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