runloop底层实现(二)mode

本文重点讨论到底什么是runloopmode及业务逻辑。

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;

};

其中CFRunLoopModeRef _currentMode;是对runloopmode的定义。

通过currentMode这个名字即可以看出runloop的mode是可以改变的。

而CFMutableSetRef _commonModes;是整个runloop所包含的mode的集合,请注意这里的_commonModes并不是等价于

kCFRunLoopCommonModes。

先看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 */

};

其中:

CFMutableSetRef _sources0;

CFMutableSetRef _sources1;

CFMutableArrayRef _observers;

CFMutableArrayRef _timers;

分别对应着runloop中常常提到的三种source、observer、timer也就是数组,存放了这三种结构。

通过CFRunLoopAddSource、CFRunLoopAddObserver、CFRunLoopAddTimer分别添加。

代码如下:

void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */

CHECK_FOR_FORK();

if (__CFRunLoopIsDeallocating(rl)) return;

if (!__CFIsValid(rls)) return;

Boolean doVer0Callout = false;

__CFRunLoopLock(rl);

//当添加的source注册到了commonmodes的时候

if (modeName == kCFRunLoopCommonModes) {

CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;

if (NULL == rl->_commonModeItems) {

//将source增加到_commonModeItems中

rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);

}

CFSetAddValue(rl->_commonModeItems, rls);

if (NULL != set) {

CFTypeRef context[2] = {rl, rls};

/* add new item to all common-modes */

//将新增的source同步到_commonModes的所有mode中

CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);

CFRelease(set);

}

} else {

......

}

}

简单来说就是如果一个source注册到了kCFRunLoopCommonModes就相当于注册到了所有在_commonModes数组里的mode之中,_commonModes里默认有两种mode::kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。UITrackingRunLoopMode是scrollview滑动时所在的mode,因此如果在滑动scrollview需要保证图片下载,音频播放等source的性能,将这些source注册为kCFRunLoopCommonModes即可。

而source到底是什么呢,在代码中可以找到如下定义:

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;

};

CFRunLoopSourceContext定义如下:

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;

其中的void (*perform)(void *info);便是soure回调方法的地址。

runloop对source调用的过程如下:

/* rl, rlm are locked on entrance and exit */

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {

......

Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);

if (sourceHandledThisLoop) {

__CFRunLoopDoBlocks(rl, rlm);

}

......

CFRUNLOOP_WAKEUP_FOR_SOURCE();

// Despite the name, this works for windows handles as well

CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);

if (rls) {

mach_msg_header_t *reply = NULL;

sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) ||   sourceHandledThisLoop;

......

}

static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) { /* DOES CALLOUT */

......

__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);

......

}

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) {

if (perform) {

perform(info);

}

getpid(); // thwart tail-call optimization

}

时间: 2024-10-23 06:29:57

runloop底层实现(二)mode的相关文章

ios Runloop

一.概念:一个Runloop就是一个事件处理的循环,用来不停的调度工作和处理输入事件,使用runloop的目的是让你的线程在有工作的时候处于工作状态,没有工作的时候处于休眠状态. 一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出.如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑是这样的: 1 2 3 4 5 6 7 function loop() {     initialize();     do {         var message = get_nex

再谈RunLoop

RunLoop 一 概述: 一句话解释RunLoop:运行任务的循环. 为什么要有RunLoop:解决交互式UI设计中的一个问题,如何快速响应用户输入,如何快速将程序运行结果输出到屏幕? 计算机是个笨蛋,同一个时间里只能做同一件事情.要么处理计算任务, 要么轮询各种I/O 接口. 那么,在没有线程的情况下,如何在计算的同时, 又能够轮询各种I/O接口,以迅捷的 和用户交互呢? CS的科学家给出的答案是:看起来够迅捷就行.人的反应速度是有上限的, 因此只要把 计算机的运行时间划分成很多小片段,小到

浅谈Runloop

RunLoop 是 iOS 和 OS X 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池.延迟回调.触摸事件.屏幕刷新等功能的. 目录 RunLoop 的概念 RunLoop 与线程的关系 RunLoop 对外的接口 RunLoop 的 Mode RunLoop 的内部逻辑 RunLoop 的底层实现 苹果用 RunLoop 实现的功能 Autore

二维数组作为函数的参数传递

如何将二维数组作为函数的参数传递,这是涉及到多维数组时经常要遇到的问题.长期来,我们往往知其然,但不知其所以然.这里简单总结一下. 1.<C程序设计>中讲到:可以用二维数组名作为实参或者形参,在被调用函数中对形参数组定义时可以指定所有维数的大小,也可以省略第一维的大小说明,如: void Func(int array[3][10]); void Func(int array[][10]); 二者都是合法而且等价,但是不能把第二维或者更高维的大小省略.两个示例程序如下: #include <

深入理解RunLoop

RunLoop 是 iOS 和 OSX 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池.延迟回调.触摸事件.屏幕刷新等功能的. RunLoop 的概念 一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出.如果我们需要一个机制,让线程能随时处理事件但并不退出,通常的代码逻辑是这样的: 1 2 3 4 5 6 7 function loop(

iOS面试题之runloop

本文围绕以下几个部分展开对runloop的叙述. 1.runloop是什么/runloop的概念? 2.NSRunLoop 和 CFRunLoopRef? 3.runloop和线程的关系? 4.runloop对外接口/runloop的几个类? 5.runloop内部逻辑? 6.runloop应用场景? 1.runloop是什么/runloop的概念? Run loops是线程相关的的基础框架的一部分.一个run loop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件.其实内部就是do

开发底层硬件应该怎么编写接口文档

开发底层硬件应该怎么编写接口文档 这几天在做超市RFID结算系统的上位机程序编写,用的是VB.NET.底层用的是别人开发好的SDK,为什么要写这一篇文章呢?最近因为手头设备的功能限制,我就在网上找其他的公司的RFID射频卡读写器,由于我是做上层开发,所以需要设备供应商提供底层SDK二次开发包,找了好多设备提供商 ,也跟他们索取各自提供的SDK,但总的来说,我还是觉得最先用的这个设备的厂家提供的SDK是最详细的,现在简单说明如下: 一.函数说明: 1.目录结构清晰: 2.函数返回值,参数用表格说明

runLoop 研究

RunLoop 是 iOS 和 OS X 开发中非常基础的一个概念,这篇文章将从 CFRunLoop 的源码入手,介绍 RunLoop 的概念以及底层实现原理.之后会介绍一下在 iOS 中,苹果是如何利用 RunLoop 实现自动释放池.延迟回调.触摸事件.屏幕刷新等功能的. 目录 RunLoop 的概念 RunLoop 与线程的关系 RunLoop 对外的接口 RunLoop 的 Mode RunLoop 的内部逻辑 RunLoop 的底层实现 苹果用 RunLoop 实现的功能 Autore

RunLoop的简单理解笔记

一句话解释RunLoop:运行任务的循环. 为什么要有RunLoop:解决交互式UI设计中的一个问题,如何快速响应用户输入,如何快速将程序运行结果输出到屏幕? 基本原理:1 将任务分解的足够细 2 每个任务运行的时间足够短. 计算机是个笨蛋,同一个时间里只能做同一件事情.要么处理计算任务, 要么轮询各种I/O 接口. 那么,在没有线程的情况下,如何在计算的同时, 又能够轮询各种I/O接口,以迅捷的 和用户交互呢? CS的科学家给出的答案是:看起来够迅捷就行.人的反应速度是有上限的, 因此只要把