RunLoop总结

作者: tom555cat | 来源:发表于2017-10-26 16:43 被阅读23次

RunLoop与线程的关系

通过[NSRunLoop currentRunLoop]来懒加载创建线程的RunLoop。
线程和RunLoop之间是一一对应的关系。

RunLoop的Mode

一个RunLoop包含若干个Mode,每个Mode又包含若干个Source/Timer/Observer。
每次调用RunLoop的主函数时,只能指定其中一个Mode,这个Mode被称为Current Mode。如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入。

RunLoop中涉及到的Source0----自定义输入源(自定义输入源)

1> 创建自定义输入源设置对应的回调函数(a>当源被加入RunLoop的回调,b>被唤醒时执行的任务回调,c>当源从RunLoop中移除的回调 )
2> 将自定义输入源加入到某一线程的RunLoop中。
3> 调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理;调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。
4> 关键需要从其他线程中能获取到想要控制的自定义输入源及其所在的RunLoop,一般在源回调a>中记录下自定义源和所在的runLoop,在其他线程里保存下来。
【自定义输入源InputSource在线程A的runLoop中,线程B持有输入源InputSource,线程B想让线程A工作了,于是给InputSource发信号(signal),唤醒(wakeup)线程A的runLoop。】
这句话还要再仔细解析。

RunLoop中涉及到Source1----NSPort(基于端口输入源)

1> 主线程中设置NSPort及NSPort的代理,在主线程的RunLoop的模式中添加NSPort。
2> 创建另一个线程,将主线程的NSPort传递给它;子线程也创建一个NSPort并设置代理。
3> 在子线程中利用主线程的NSPort和子线程的NSPort创建NSPortMessage来传递数据,在主线程的NSPort的代理中会接收到这个NSPortMessage。

定时源,也就是定时器

Observer观察者

每个Oberserver都包含一个回调(函数指针),当RunLoop的状态发生变化时,观察者就能通过回调接收到这个变化。可以观察到如下时间点:

The entrance to the run loop.
When the run loop is about to process a timer.
When the run loop is about to process an input source.
When the run loop is about to go to sleep.
When the run loop has woken up, but before it has processed the event that woke it up.
The exit from the run loop.

增加观察者只能使用Core Foundation,
添加的Observer的回调是同步执行的,执行完回调,才会执行其他东西(因为是在同一个线程内)。

- (void)addObserverToCurrentRunLoop
{
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopAfterWaiting | kCFRunLoopEntry, true, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        NSLog(@"睡眠之前!");
        [NSThread sleepForTimeInterval:5.0];
        NSLog(@"睡眠之后!");
    });
    
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFRunLoopAddObserver(runLoop, observer, kCFRunLoopCommonModes);
}

一个observer可以监控多个RunLoop的状态。

Mode,Mode Item,CommonModes, CommonModeItems

Source/Timer/Observer统称为mode item。如果一个mode中一个item都没有,则runLoop会直接退出,不进入循环。

CommonMode和CommonModeItem的联系

添加到"NSRunLoopCommandModes"的Mode item会被所有标记为"Common"的Mode所共享;
默认的NSDefaultRunLoopMode和NSEventTrackingRunLoopMode就是"Common"Mode。

应用场景:kCFRunLoopDefaultMode和UITrackingRunLoopMode都具有"Common"属性;将定时源加入到kCFRunLoopDefaultMode和UITrackingRunLoopMode时,默认情况下和滑动时都响应,如果把定时源计入到CommonModeItem时,也可以都响应。

AutoReleasePool与RunLoop的关系

App启动后,在主线程的RunLoop中注册了两个Observer,
1> 第一个Observer监控的事件是Entry(即将进入RunLoop),回调内部会调用_objc_autoreleasePoolPush()创建自动释放池,其order实-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
2> 第二个Observer监控了两个事件:BeforeWaiting(准备进入休眠)时调用_objc_autoreleasePoolPop()和_objc_autoreleasePoolPush()释放旧的池并创建新池;Exit(即将退出RunLoop时)调用_objc_autoreleasePoopPop()来释放自动释放池。这个Observer的order是2147483647,优先级最低,保证其释放池子发生在其他所有回调之后。

RunLoop与手势事件的关系

在ViewController中添加一个UITapGestureRecognizer,然后在对应的selector处"-[ViewController gesture:]"打断点,主线程的调用栈如下所示:

frame #0: 0x000000010cac165e RunLoopTest`-[ViewController gesture:](self=0x00007fdb1a416ad0, _cmd="gesture:", gr=0x00006040001fa400) at ViewController.m:32
frame #1: 0x000000010e7a554f UIKit`-[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 57
frame #2: 0x000000010e7ae324 UIKit`_UIGestureRecognizerSendTargetActions + 109
frame #3: 0x000000010e7abb6c UIKit`_UIGestureRecognizerSendActions + 307
frame #4: 0x000000010e7aadc0 UIKit`-[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:] + 859
frame #5: 0x000000010e78fe24 UIKit`_UIGestureEnvironmentUpdate + 1329
frame #6: 0x000000010e78f8a7 UIKit`-[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:] + 484
frame #7: 0x000000010e78e9a9 UIKit`-[UIGestureEnvironment _updateGesturesForEvent:window:] + 281
frame #8: 0x000000010e2247ab UIKit`-[UIWindow sendEvent:] + 4064
frame #9: 0x000000010e1c8310 UIKit`-[UIApplication sendEvent:] + 352
frame #10: 0x000000010eb096af UIKit`__dispatchPreprocessedEventFromEventQueue + 2796
* frame #11: 0x000000010eb0c2c4 UIKit`__handleEventQueueInternal + 5949
frame #12: 0x000000010dcd2bb1 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #13: 0x000000010dcb7574 CoreFoundation`__CFRunLoopDoSources0 + 468
frame #14: 0x000000010dcb6a6f CoreFoundation`__CFRunLoopRun + 1263
frame #15: 0x000000010dcb630b CoreFoundation`CFRunLoopRunSpecific + 635
frame #16: 0x0000000112ea4a73 GraphicsServices`GSEventRunModal + 62
frame #17: 0x000000010e1ad057 UIKit`UIApplicationMain + 159
frame #18: 0x000000010cac16ef RunLoopTest`main(argc=1, argv=0x00007ffee313e010) at main.m:14
frame #19: 0x000000011178d955 libdyld.dylib`start + 1

通过栈帧frame#13和#12的__CFRunLoopDoSources0和__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION可以发现是触摸的回调是通过主线程RunLoop的source 0触发的,而这个source 0是是由线程com.apple.uikit.eventfetch-thread激活的,大致流程如下,最终进入到了主线程的__handleEventQueueInternal函数中,如果__handleEventQueueInternal中不处理事件,调用-[UIEventDispatcher signalNextDelivery]再进行循环。

触摸事件处理流程

Timer与RunLoop的关系

NSTimer就是Mode中的Timer。

PerformSelector与RunLoop的关系

调用performSelector:afterDelay:会创建一个Timer添加到当前线程的RunLoop中。如果当前线程没有RunLoop,则这个方法会失效。【在global队列中尝试调用这个方法,没有执行,说明dispatch_get_global_queue中涉及到的线程没有runLoop。】

Sources, timers, and observers get registered to one or more run loop modes and only run when the run loop is running in one of those modes.

Common modes are a set of run loop modes for which you can define a set of sources, timers, and observers that are shared by these modes.

Instead of registering a source, for example, to each specific run loop mode, you can register it once to the run loop’s common pseudo-mode and it will be automatically registered in each run loop mode in the common mode set.

Likewise, when a mode is added to the set of common modes, any sources, timers, or observers already registered to the common pseudo-mode are added to the newly added common mode.

Once a mode is added to the set of common modes, it cannot be removed.
The Add, Contains, and Remove functions for sources, timers, and observers operate on a run loop’s set of common modes when you use the constant kCFRunLoopCommonModes for the run loop mode.

相关文章

  • RunLoop

    RunLoop 文章已经很多了,结合各大文章做个总结 什么是 RunLoop RunLoop 人如其名,run 跑...

  • iOS RunLoop 总结以及相关面试题解答

    iOS RunLoop 总结以及相关面试题解答 iOS RunLoop 总结以及相关面试题解答

  • NSRunLoop

    【iOS程序启动与运转】- RunLoop个人小结 RunLoop总结:RunLoop的应用场景(三) 走进Run...

  • iOS开发经验(18)-Runloop

    目录 Runloop RunLoop 与线程 个人理解总结 应用场景 1. 什么是RunLoop 基本作用 保持程...

  • 探寻RunLoop的本质

    iOS底层原理总结 - RunLoop 面试题 讲讲 RunLoop,项目中有用到吗? RunLoop内部实现逻辑...

  • iOS面试点文章链接

    runtime基础方法、用法、消息转发、super: runtime 完整总结 runloop源码、runloop...

  • RunLoop数据结构、RunLoop的实现机制、RunLoop

    推荐阅读:备战2020——iOS全新面试题总结 RunLoop概念 RunLoop的数据结构 RunLoop的Mo...

  • RunLoop

    iOS刨根问底-深入理解RunLoop runloop 和线程有什么关系 iOS 多线程:RunLoop详细总结

  • RunLoop 的相关概念

    此篇为对 RunLoop 的相关概念的总结,主要介绍 RunLoop 的一些概念 RunLoop 简介 RunLo...

  • RunLoop相关

    iOS底层原理总结 - RunLoop解密 Runloop Runloop是一种在当前线程,持续调度各种任务的运行...

网友评论

    本文标题:RunLoop总结

    本文链接:https://www.haomeiwen.com/subject/yevouxtx.html