CFRunLoopMode 和 CFRunLoop 的结构大致如下:
struct __CFRunLoopMode {
CFStringRef _name; // Mode Name, 例如 @"kCFRunLoopDefaultMode"
CFMutableSetRef _sources0; // Set
CFMutableSetRef _sources1; // Set
CFMutableArrayRef _observers; // Array
CFMutableArrayRef _timers; // Array
...
};
struct __CFRunLoop {
CFMutableSetRef _commonModes; // Set
CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>
CFRunLoopModeRef _currentMode; // Current Runloop Mode
CFMutableSetRef _modes; // Set
...
};
这里有个概念叫 CommonModes
:
一个 Mode 可以将自己标记为Common
属性(通过将其 ModeName 添加到 RunLoop 的 commonModes
中)。每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems
里的 Source/Observer/Timer
同步到具有 Common
标记的所有Mode里。
CFRunLoop对外暴露的管理 Mode 接口只有下面2个:
CFRunLoopAddCommonMode(CFRunLoopRef runloop, CFStringRef modeName);
CFRunLoopRunInMode(CFStringRef modeName, ...);
Mode 暴露的管理 mode item 的接口有下面几个:
CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
你只能通过 mode name`来操作内部的 mode,
当你传入一个新的 mode name 但 RunLoop 内部没有对应 mode 时,RunLoop会自动帮你创建对应的 CFRunLoopModeRef。
对于一个 RunLoop 来说,其内部的 mode 只能增加不能删除。
苹果公开提供的 Mode 有两个:
kCFRunLoopDefaultMode (NSDefaultRunLoopMode)
UITrackingRunLoopMode
同时苹果还提供了一个操作 Common 标记的字符串:
kCFRunLoopCommonModes (NSRunLoopCommonModes)
你可以用这个字符串来操作 Common Items,或标记一个 Mode 为 “Common”。使用时注意区分这个字符串和其他 mode name。
下面来看下Runloop的mode图:
在RunLoop中,假如在mode1中运行,那么在mode2中事件的回调就会接收不到,RunLoop只接受在当前mode中的回调。
那么这里有一个经典问题,当我们在滑动列表时,为什么会出现cell上的定时器停止的情况以及如何解决?
原因:
因为在列表滑动的时候当前RunLoop
的mode
从Default
切换到了Tracking
,所以导致原来mode中的事件回调接收不到,想要解决便可将其加入commonModes
中
有时你需要一个 Timer,在两个 Mode 中都能得到回调。
- 一种办法就是将这个 Timer 分别加入这两个 Mode。
- 还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的
commonModeItems
中。commonModeItems
被RunLoop
自动更新到所有具有Common
属性的 Mode 里去。