1.runloop源码
2.对象介绍
接下来的对象介绍,可以借助这个图理清对象间的关系
// 一个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
对象中有version0
、version1
分别对应着_sources0
和_sources1
- 包含了observer、timer
-
CFStringRef _name
就是mode的名字,如:kCFRunLoopDefaultMode
- 有几种mode图片来源
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和线程是一一对应的关系
3.3.获取主线程loop和获取当前的loop
参考文档
强力推荐
RunLoop系列之要点提炼
RunLoop系列之源码分析
iOS刨根问底-深入理解RunLoop
深入理解 RunLoop