RunLoop 的 Mode

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图:


image.png

在RunLoop中,假如在mode1中运行,那么在mode2中事件的回调就会接收不到,RunLoop只接受在当前mode中的回调。

那么这里有一个经典问题,当我们在滑动列表时,为什么会出现cell上的定时器停止的情况以及如何解决?

原因:
因为在列表滑动的时候当前RunLoopmodeDefault切换到了Tracking,所以导致原来mode中的事件回调接收不到,想要解决便可将其加入commonModes

有时你需要一个 Timer,在两个 Mode 中都能得到回调。

image.png

  • 一种办法就是将这个 Timer 分别加入这两个 Mode。
  • 还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 commonModeItems中。commonModeItemsRunLoop 自动更新到所有具有Common属性的 Mode 里去。

CommonMode的特殊性

CommonMode.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容