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
    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 的 Mode_第1张图片
image.png

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

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

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

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

RunLoop 的 Mode_第2张图片
image.png

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

CommonMode的特殊性

RunLoop 的 Mode_第3张图片
CommonMode.png

你可能感兴趣的:(RunLoop 的 Mode)