你是否厌倦了阅读枯燥的源码?本文试图通过运行 App 实例来分析 Runloop 的内在构成。
首先,我们需要拷贝部分源码过来,可以粘贴到你的.m 文件上面:
typedef struct __CFRuntimeBase {
uintptr_t _cfisa;
uint8_t _cfinfo[4];
#if __LP64__
uint32_t _rc;
#endif
} CFRuntimeBase;
typedef struct ___CFPortSet {
uint16_t used;
uint16_t size;
mach_port_t *handles;
uint32_t lock; // insert and remove must be thread safe, like the Mach calls
} *__CFPortSet;
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 */
};
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 */
};
struct _CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
mach_port_t _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile uint32_t *_perRunData; // reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
struct _CFRunLoopMode *_currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
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;
};
通过这些数据结构,你大致了解或重温了 Runloop 的内存结构,下面开始运行分析。
通过打印 lp->_commonModes,输出如下:
_lp->_commonModes:
{(
UITrackingRunLoopMode,
kCFRunLoopDefaultMode
)}
新建的 Runloop 的 commonModes set 里默认添加了 kCFRunLoopDefaultMode, 现在我们看到主线程的 Runloop 还添加了 UITrackingRunLoopMode。
我们知道 source,timer,observer 在 kCFRunLoopCommonModes 下添加到 Runloop 会被添加 commonModeItems 里,那么在主线程下,这里面有啥呢?
_lp->_commonModeItems:
{(
<CFRunLoopSource 0x1c01798c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = <redacted> (0x183f5daec)}},
<CFRunLoopSource 0x1c4179140 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x1c414c290, callout = <redacted> (0x18c63a1b8)}},
<CFRunLoopSource 0x1c4179380 [0x1b42c2538]>{signalled = No, valid = Yes, order = -2, context = <CFRunLoopSource context>{version = 0, info = 0x1c4259cb0, callout = <redacted> (0x18c63a1c0)}},
<CFRunLoopSource 0x1c41792c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 13063, subsystem = 0x1ad95d5d0, context = 0x0}},
<CFRunLoopSource 0x1c0179e00 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 42251, subsystem = 0x1ad977c68, context = 0x1c002aa80}},
<CFRunLoopObserver 0x1c41350e0 [0x1b42c2538]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = <redacted> (0x18bf494c4), context = <CFRunLoopObserver context 0x1c40c2a00>},
<CFRunLoopSource 0x1c0179980 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x4e03, callout = <redacted> (0x183f60238)}},
<CFRunLoopSource 0x1c0179740 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 24595, subsystem = 0x1acc62ef0, context = 0x1c00a81c0}},
<CFRunLoopSource 0x1c4179500 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x1c40a8580, callout = <redacted> (0x1848e7bf0)}},
<CFRunLoopObserver 0x1c4135720 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = <redacted> (0x1861c63f4), context = <CFRunLoopObserver context 0x0>},
<CFRunLoopObserver 0x1c4135680 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = <redacted> (0x18bf49650), context = <CFArray 0x1c425ab50 [0x1b42c2538]>{type = mutable-small, count = 1, values = (
0 : <0x104854048>
)}},
<CFRunLoopObserver 0x1c41355e0 [0x1b42c2538]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = <redacted> (0x18bf49650), context = <CFArray 0x1c425ab50 [0x1b42c2538]>{type = mutable-small, count = 1, values = (
0 : <0x104854048>
)}},
<CFRunLoopObserver 0x1c4135540 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = <redacted> (0x18bf494cc), context = <CFRunLoopObserver context 0x104c02ba0>},
<CFRunLoopObserver 0x1c41354a0 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = <redacted> (0x18c07c9fc), context = <CFRunLoopObserver context 0x104c02ba0>}
)}
里面有 8 个 sources 和 6 个 observers,
_lp->_modes:
{(
<CFRunLoopMode 0x1c0188a20 [0x1b42c2538]>{name = UITrackingRunLoopMode, port set = 0x2b03, queue = 0x1c014c080, source = 0x1c0188af0 (not fired), timer port = 0x2d03,
sources0 = <CFBasicHash 0x1c024ed00 [0x1b42c2538]>{type = mutable set, count = 4,
entries =>
0 : <CFRunLoopSource 0x1c01798c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = <redacted> (0x183f5daec)}}
3 : <CFRunLoopSource 0x1c4179380 [0x1b42c2538]>{signalled = No, valid = Yes, order = -2, context = <CFRunLoopSource context>{version = 0, info = 0x1c4259cb0, callout = <redacted> (0x18c63a1c0)}}
5 : <CFRunLoopSource 0x1c4179500 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x1c40a8580, callout = <redacted> (0x1848e7bf0)}}
6 : <CFRunLoopSource 0x1c4179140 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x1c414c290, callout = <redacted> (0x18c63a1b8)}}
}
,
sources1 = <CFBasicHash 0x1c024edc0 [0x1b42c2538]>{type = mutable set, count = 4,
entries =>
0 : <CFRunLoopSource 0x1c0179980 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x4e03, callout = <redacted> (0x183f60238)}}
1 : <CFRunLoopSource 0x1c41792c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 13063, subsystem = 0x1ad95d5d0, context = 0x0}}
4 : <CFRunLoopSource 0x1c0179740 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 24595, subsystem = 0x1acc62ef0, context = 0x1c00a81c0}}
6 : <CFRunLoopSource 0x1c0179e00 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 42251, subsystem = 0x1ad977c68, context = 0x1c002aa80}}
}
,
observers = (
"<CFRunLoopObserver 0x1c41355e0 [0x1b42c2538]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = <redacted> (0x18bf49650), context = <CFArray 0x1c425ab50 [0x1b42c2538]>{type = mutable-small, count = 1, values = (nt0 : <0x104854048>n)}}",
"<CFRunLoopObserver 0x1c41350e0 [0x1b42c2538]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = <redacted> (0x18bf494c4), context = <CFRunLoopObserver context 0x1c40c2a00>}",
"<CFRunLoopObserver 0x1c41354a0 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = <redacted> (0x18c07c9fc), context = <CFRunLoopObserver context 0x104c02ba0>}",
"<CFRunLoopObserver 0x1c4135720 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = <redacted> (0x1861c63f4), context = <CFRunLoopObserver context 0x0>}",
"<CFRunLoopObserver 0x1c4135540 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = <redacted> (0x18bf494cc), context = <CFRunLoopObserver context 0x104c02ba0>}",
"<CFRunLoopObserver 0x1c4135680 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = <redacted> (0x18bf49650), context = <CFArray 0x1c425ab50 [0x1b42c2538]>{type = mutable-small, count = 1, values = (nt0 : <0x104854048>n)}}"
),
timers = (null),
currently 545921038 (8000932696234) / soft deadline in: 7.68614003e+11 sec (@ -1) / hard deadline in: 7.68614003e+11 sec (@ -1)
},
,
<CFRunLoopMode 0x1c0188bc0 [0x1b42c2538]>{name = GSEventReceiveRunLoopMode, port set = 0x5003, queue = 0x1c014c600, source = 0x1c0188c90 (not fired), timer port = 0x2e03,
sources0 = <CFBasicHash 0x1c024ee50 [0x1b42c2538]>{type = mutable set, count = 1,
entries =>
0 : <CFRunLoopSource 0x1c01798c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = <redacted> (0x183f5daec)}}
}
,
sources1 = <CFBasicHash 0x1c024edf0 [0x1b42c2538]>{type = mutable set, count = 1,
entries =>
0 : <CFRunLoopSource 0x1c0179a40 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x4e03, callout = <redacted> (0x183f60238)}}
}
,
observers = (null),
timers = (null),
currently 545921038 (8000932859869) / soft deadline in: 7.68614003e+11 sec (@ -1) / hard deadline in: 7.68614003e+11 sec (@ -1)
},
,
<CFRunLoopMode 0x1c0188540 [0x1b42c2538]>{name = kCFRunLoopDefaultMode, port set = 0x1803, queue = 0x1c014c3f0, source = 0x1c0188610 (not fired), timer port = 0x1903,
sources0 = <CFBasicHash 0x1c024ed60 [0x1b42c2538]>{type = mutable set, count = 4,
entries =>
0 : <CFRunLoopSource 0x1c01798c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = <redacted> (0x183f5daec)}}
3 : <CFRunLoopSource 0x1c4179380 [0x1b42c2538]>{signalled = No, valid = Yes, order = -2, context = <CFRunLoopSource context>{version = 0, info = 0x1c4259cb0, callout = <redacted> (0x18c63a1c0)}}
5 : <CFRunLoopSource 0x1c4179500 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x1c40a8580, callout = <redacted> (0x1848e7bf0)}}
6 : <CFRunLoopSource 0x1c4179140 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x1c414c290, callout = <redacted> (0x18c63a1b8)}}
}
,
sources1 = <CFBasicHash 0x1c024ed90 [0x1b42c2538]>{type = mutable set, count = 4,
entries =>
0 : <CFRunLoopSource 0x1c0179980 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 1, info = 0x4e03, callout = <redacted> (0x183f60238)}}
1 : <CFRunLoopSource 0x1c41792c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 13063, subsystem = 0x1ad95d5d0, context = 0x0}}
4 : <CFRunLoopSource 0x1c0179740 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 24595, subsystem = 0x1acc62ef0, context = 0x1c00a81c0}}
6 : <CFRunLoopSource 0x1c0179e00 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource MIG Server> {port = 42251, subsystem = 0x1ad977c68, context = 0x1c002aa80}}
}
,
observers = (
"<CFRunLoopObserver 0x1c41355e0 [0x1b42c2538]>{valid = Yes, activities = 0x1, repeats = Yes, order = -2147483647, callout = <redacted> (0x18bf49650), context = <CFArray 0x1c425ab50 [0x1b42c2538]>{type = mutable-small, count = 1, values = (nt0 : <0x104854048>n)}}",
"<CFRunLoopObserver 0x1c41350e0 [0x1b42c2538]>{valid = Yes, activities = 0x20, repeats = Yes, order = 0, callout = <redacted> (0x18bf494c4), context = <CFRunLoopObserver context 0x1c40c2a00>}",
"<CFRunLoopObserver 0x1c41354a0 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 1999000, callout = <redacted> (0x18c07c9fc), context = <CFRunLoopObserver context 0x104c02ba0>}",
"<CFRunLoopObserver 0x1c4135720 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2000000, callout = <redacted> (0x1861c63f4), context = <CFRunLoopObserver context 0x0>}",
"<CFRunLoopObserver 0x1c4135540 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2001000, callout = <redacted> (0x18bf494cc), context = <CFRunLoopObserver context 0x104c02ba0>}",
"<CFRunLoopObserver 0x1c4135680 [0x1b42c2538]>{valid = Yes, activities = 0xa0, repeats = Yes, order = 2147483647, callout = <redacted> (0x18bf49650), context = <CFArray 0x1c425ab50 [0x1b42c2538]>{type = mutable-small, count = 1, values = (nt0 : <0x104854048>n)}}"
),
timers = <CFArray 0x1c40a8760 [0x1b42c2538]>{type = mutable-small, count = 1, values = (
0 : <CFRunLoopTimer 0x1c017c740 [0x1b42c2538]>{valid = Yes, firing = No, interval = 0, tolerance = 0, next fire date = 545920896 (-141.531507 @ 7997537050952), callout = (NSTimer) [_UISystemGestureGateGestureRecognizer _timeOut] (0x182b0337c / 0x18bf41d7c) (/System/Library/Frameworks/UIKit.framework/UIKit), context = <CFRunLoopTimer context 0x1c00317e0>}
)},
currently 545921038 (8000932867535) / soft deadline in: 7.68614336e+11 sec (@ 7997537050952) / hard deadline in: 7.68614336e+11 sec (@ 7997537050952)
},
,
<CFRunLoopMode 0x1c0189240 [0x1b42c2538]>{name = kCFRunLoopCommonModes, port set = 0xa907, queue = 0x1c014cb80, source = 0x1c0189310 (not fired), timer port = 0x5703,
sources0 = (null),
sources1 = (null),
observers = (null),
timers = (null),
currently 545921038 (8000933166060) / soft deadline in: 7.68614003e+11 sec (@ -1) / hard deadline in: 7.68614003e+11 sec (@ -1)
},
)}
归类分析我们发现 4 种 mode:
[_UISystemGestureGateGestureRecognizer _timeOut]
仔细比较后我们发现:
<CFRunLoopSource 0x1c01798c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = -1, context = <CFRunLoopSource context>{version = 0, info = 0x0, callout = <redacted> (0x183f5daec)}}
被添加到了 3 个 mode 里,也就是所有 mode 里。<CFRunLoopMode 0x1c0188a20 [0x1b42c2538]>{name = UITrackingRunLoopMode, port set = 0x2b03, queue = 0x1c014c080, source = 0x1c0188af0 (not fired), timer port = 0x2d03,
<CFRunLoopMode 0x1c0188540 [0x1b42c2538]>{name = kCFRunLoopDefaultMode, port set = 0x1803, queue = 0x1c014c3f0, source = 0x1c0188610 (not fired), timer port = 0x1903,
这两个 mode 所在队列和 timerSource 是不同的。
<CFRunLoopMode 0x1c0188540 [0x1b42c2538]>{name = kCFRunLoopDefaultMode, port set = 0x1803, queue = 0x1c014c3f0, source = 0x1c0188610 (not fired), timer port = 0x1903,
通常为默认的 kCFRunLoopDefaultMode。
新建一个子线程如下:
- (void)main{
NSTimer *timer = [NSTimer timerWithTimeInterval:6 target:self selector:@selector(say) userInfo:nil repeats:YES];
timer.tolerance = 1;
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
struct _CFRunLoop *lp = CFRunLoopGetCurrent();
_lp2 = lp;
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
_lp2->_commonModes:
{(
kCFRunLoopDefaultMode
)}
为空,因为我们没有在 commonMode 下运行
lp2->_modes:
{(
<CFRunLoopMode 0x1c4187f90 [0x1b42c2538]>{name = kCFRunLoopDefaultMode, port set = 0xa203, queue = 0x1c41455f0, source = 0x1c4188060 (not fired), timer port = 0xa103,
sources0 = (null),
sources1 = (null),
observers = (null),
timers = <CFArray 0x1c40a4b00 [0x1b42c2538]>{type = mutable-small, count = 1, values = (
0 : <CFRunLoopTimer 0x1c41747c0 [0x1b42c2538]>{valid = Yes, firing = Yes, interval = 6, tolerance = 1, next fire date = 545924825 (-345.269891 @ 8091831849828), callout = (NSTimer) [ViewController say] (0x182b0337c / 0x10416a3e8) (/var/containers/Bundle/Application/97D498CA-C786-46B3-A8AB-0B3841AAE115/runloopDemo.app/runloopDemo), context = <CFRunLoopTimer context 0x1c4035ca0>}
)},
currently 545925171 (8100117929266) / soft deadline in: 7.68613999e+11 sec (@ -1) / hard deadline in: 7.68613999e+11 sec (@ -1)
},
)}
果然只有一个我们当前运行的 kCFRunLoopDefaultMode,并且其 sources 为空,只有一个我们的 timer。
<CFRunLoopMode 0x1c4187f90 [0x1b42c2538]>{name = kCFRunLoopDefaultMode, port set = 0xa203, queue = 0x1c41455f0, source = 0x1c4188060 (not fired), timer port = 0xa103,
无需多讲,如上。
如果把 NSTimer 添加到 NSRunLoopCommonModes,上面的数据除了_commonModeItems 里多了这个 NSTimer,其他的都没变。
- (void)main{
[[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
struct _CFRunLoop *lp = CFRunLoopGetCurrent();
_lp2 = lp;
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
}
_lp2->_commonModes:
{(
kCFRunLoopDefaultMode
)}
lp2->_commonModeItems:
{(
<CFRunLoopSource 0x1c016c6c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x1c000ef70, callout = <redacted> (0x182b01f98)}}
)}
这里有个 source0,在哪里添加进来的呢?读者可以想一想
lp2->_modes:
{(
<CFRunLoopMode 0x1c41893e0 [0x1b42c2538]>{name = kCFRunLoopDefaultMode, port set = 0x5d03, queue = 0x1c4146d50, source = 0x1c41894b0 (not fired), timer port = 0x5f03,
sources0 = <CFBasicHash 0x1c4457880 [0x1b42c2538]>{type = mutable set, count = 1,
entries =>
1 : <CFRunLoopSource 0x1c016c6c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = 0, context = <CFRunLoopSource context>{version = 0, info = 0x1c000ef70, callout = <redacted> (0x182b01f98)}}
}
,
sources1 = <CFBasicHash 0x1c4457520 [0x1b42c2538]>{type = mutable set, count = 1,
entries =>
0 : <CFRunLoopSource 0x1c416e7c0 [0x1b42c2538]>{signalled = No, valid = Yes, order = 200, context = <CFMachPort 0x1c4146e00 [0x1b42c2538]>{valid = Yes, port = 6003, source = 0x1c416e7c0, callout = __NSFireMachPort (0x182ae5a58), context = <CFMachPort context 0x1c44573d0>}}
}
,
observers = (null),
timers = (null),
currently 545926298 (8127170276617) / soft deadline in: 7.68613998e+11 sec (@ -1) / hard deadline in: 7.68613998e+11 sec (@ -1)
},
)}
<CFRunLoopMode 0x1c41893e0 [0x1b42c2538]>{name = kCFRunLoopDefaultMode, port set = 0x5d03, queue = 0x1c4146d50, source = 0x1c41894b0 (not fired), timer port = 0x5f03