自下而上依次为: 1.加载dyld动态库以及dyld加载其他动态库 2.执行main函数 3.runloop处理source,此处是source0 4.UIKit收到source0后处理touch...所谓的向外是相对于runloop的,其实就是runLoop向上层回调,通过回调函数runloop可以通知上层runloop当前处于什么状态或正在处理什么事件。...RunLoop 触发 Source0 (非port) 回调 // __CFRunLoopDoSources0函数内部会调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0..._PERFORM_FUNCTION__函数 // __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__函数会调用source0...则CFRunLoopWakeUp函数直接返回,不再执行唤醒操作。
自下而上依次为: 1.加载dyld动态库以及dyld加载其他动态库 2.执行main函数 3.runloop处理source,此处是source0 4.UIKit收到source0后处理touch事件...所谓的向外是相对于runloop的,其实就是runLoop向上层回调,通过回调函数runloop可以通知上层runloop当前处于什么状态或正在处理什么事件。...RunLoop 触发 Source0 (非port) 回调 // __CFRunLoopDoSources0函数内部会调用__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0..._PERFORM_FUNCTION__函数 // __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__函数会调用source0...则CFRunLoopWakeUp函数直接返回,不再执行唤醒操作。
二、RunLoop的数据结构 NSRunLoop(Foundation)是CFRunLoop(CoreFoundation)的封装,提供了面向对象的API RunLoop 相关的主要涉及五个类: CFRunLoop...、source1、observers、timers构成 3、CFRunLoopSource 分为source0和source1两种 source0: 即非基于port的,也就是用户触发的事件。...如果线程当前正在处理繁重的任务,就有可能导致Timer本次延时,或者少执行一次)。...RunLoop通过mach_msg()函数接收、发送消息。它的本质是调用函数mach_msg_trap(),相当于是一个系统调用,会触发内核状态切换。...当我们在子请求数据的同时滑动浏览当前页面,如果数据请求成功要切回主线程更新UI,那么就会影响当前正在滑动的体验。
CFRunLoopModeRef _currentMode; CFMutableSetRef _modes; ... } 其中_currentMode即代表当前RunLoop对象正在执行的...按钮点击的调用栈 从上图中可以看到程序在18处执行main函数,17执行UIApplicationMain函数,这就是程序启动过程,16是系统内部事件,15调用CFRunLoopRunSpecific后文会详细讲解该函数...,14开始执行RunLoop进入循环,13开始处理source0这个source0就是点击按钮的事件,11是真正执行source0的函数,10-0就是点击事件的整个转发处理过程,最终交由我们自定义的回调方法进行处理...在前文给了一个点击按钮的调用栈运行图,可以发现执行source0事件时是调用了一个非常长的函数来处理,为了方便查看调用栈执行的顺序,深入理解RunLoop https://blog.ibireme.com...__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
所以,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。...线程执行了这个函数后,就会一直处于这个函数内部 "接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。...Source 有两个版本:Source0 和 Source1。 Source0 只包含了一个回调(函数指针),它并不能主动触发事件。...__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK...这个函数里会遍历所有待处理的 UIView/CALayer 以执行实际的绘制和调整,并更新 UI 界面。
触发 Source0 (非基于port的) 回调。...__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK...,记录一下时间,并把正在执行任务的标记置为YES,将要进入睡眠状态时,将正在执行任务的标记置为NO。...monitor.excuting) { return; } // 如果主线程正在执行任务,并且这一次loop 执行到 现在还没执行完,那就需要计算时间差...,如果当前正在执行任务的状态为YES,并且从开始执行到现在的时间大于阙值,则把堆栈信息保存下来,便于后面处理。
可以从CF框架源码 的 CFRunLoop.h和CFRunLoop.c,看看 苹果对 CFRunLoopRef 的定义。...CFRunLoopRef是 结构体__CFRunLoop *的重命名,由 typedef struct __CFRunLoop * CFRunLoopRef; 可知; __CFRunLoop 的定义:...这个函数的作用与 通过 key 从 NSDictionary 获取Value 极为相似。...通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。...RunLoop 触发 Source0 (非port) 回调。
一、CFRunLoop部分源码 阅读源码:CFRunLoop.c CFRunLoopRef CFRunLoopGetCurrent(void) { CHECK_FOR_FORK(); CFRunLoopRef...Sources0 触摸事件处理 performSelector:OnThread: Source1 基于Port的线程间通信 系统事件的捕捉(source1 捕捉 source0处理) Timers NSTimer...kCFRunLoopExit = (1UL << 7), //即将推出Loop kCFRunLoopAllActivities = 0x0FFFFFFFU }; 如果model里没有任何source0...如果Source有block 处理Source0的block ->__CFRunLoopDoSources0 Boolean sourceHandledThisLoop = __CFRunLoopDoSources0...//可以看方法注释 CFRunloopRunInMode 第三个参数是 执行完source后是否退出 结构体初始化最好赋值为{0} 避免内存垃圾
如果没有RunLoop,UIApplicationMain函数执行完毕之后将直接返回,也就没有程序持续运行一说了。 四....RunLoop结构体 通过源码我们找到__CFRunLoop结构体 struct __CFRunLoop { CFRuntimeBase _base; pthread_mutex_t _...内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ __CFRunLoopDoSources0 内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0...__ GCD 调用 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ __CFRunLoopDoSource1 内部调用 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1...并且RunLoop中要至少有一个Timer 或 一个Source 保证RunLoop不会因为空转而退出,因此在创建的时候直接加入,如果没有加入Timer或者Source,或者只加入一个监听者,运行程序会崩溃
image.png Runloop的创建 typedef struct __CFRunLoop * CFRunLoopRef; struct __CFRunLoop { CFRuntimeBase...只要result 没有stop或者返回finish就只周而复始的一直执行CFRunLoopRunSpecific函数。...Source共在2种类型:Source0和Source1,Source0只包含了一个回调(函数指针),它并不能主动触发事件。...runMode:beforeDate:则对应CFRunLoopRunInMode(mode,limiteDate,true)方法,只执行一次,执行完就退出。...),执行完并不会退出,继续下一次RunLoop直到timeout。
Runloop对象会提供一个入口函数(定义如下),当程序执行该入口函数后,就会进入一个do-while循环,这就是Runloop能使线程常驻的原因所在。...如果找到CFRunloop的源码,我们可以看到,CFRunloop.c的源码也就3900行左右。所以说Runloop其实比Runtime要简单多了!...是一个指向__CFRunLoop结构体的指针,__CFRunLoop结构体的定义如下: struct __CFRunLoop { CFRuntimeBase _base; pthread_mutex_t...当一个source0事件准备执行的时候,必须要先把它标记为signal状态。...CFRunloopTimer包含一个时间长度和一个回调(函数指针),当其加入到Runloop时,Runloop会注册对应的时间点,当时间点到时,Runloop会被唤醒以执行那个回调。
Source0事件:处理如UIEvent,CFSocket这类事件。需要手动触发。...触摸事件其实是Source1接收系统事件后在回调 __IOHIDEventSystemClientQueueCallback() 内触发的 Source0,Source0 再触发的 _UIApplicationHandleEventQueue...source0一定是要唤醒runloop及时响应并执行的,如果runloop此时在休眠等待系统的 mach_msg事件,那么就会通过source1来唤醒runloop执行。...试想下面这几种情况: 两个线程同时设置同一个背景图片,那么很有可能因为当前图片被释放了两次而导致应用崩溃。...当Oberver监听的事件到来时,回调执行函数中会遍历所有待处理的UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。
通知Observers: 即将触发Source(非基于Port的,Source0)回调。...触发Source0(非基于port的)回调。..._CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION_(source0); /// 5....如果是被dispatch唤醒的,执行所有调用dispatch_async等方法放入main queue的block _CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE...myRunLoopObserver中注释 //第三个参数用于标识该observer是在第一次进入run loop时执行还是每次进入run loop处理时均执行 //第四个参数用于设置该observer
所以,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行上面 Event Loop 的逻辑。...线程执行了这个函数后,就会一直处于这个函数内部 "接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。 ...Source有两个版本:Source0 和 Source1。 • Source0 只包含了一个回调(函数指针),它并不能主动触发事件。...__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK...实际上,start 这个函数的内部会获取CurrentRunLoop,然后在其中的DefaultMode添加了4个 Source0 (即需要手动触发的Source)。
底层是生成这种时间源并加 入到当前Runloop中,当时间点到时,Runloop被主动唤醒执行回调操作。 ? 3. ...我们来看下 CFRunloop 以及 CFRunloopMode的定义: ?...CFRunloop.c 的源码可以在这里https://opensource.apple.com/source/CF/CF-855.17/CFRunLoop.c.auto.html看到,下面是关键部分的源代码...在主线程执行的代码,通常是写在诸如事件回调、Timer回调内的。...随后苹果注册的那个Source1就会触发回调__IOHIDEventSystemClientQueueCallback(),在回调中触发source0事件源,source0的回调_UIApplicationHandleEventQueue
所以我们这里着重介绍CFRunLoop,毕竟我们能拿到CFRunLoop的源码。...runLoop中的block __CFRunLoopDoBlocks(rl, rlm); // 第四步,执行source0中的源事件 Boolean...1.source0,source1 首先这个源事件分为两种,一种是不基于端口的source0,一直是基于端口的source1。 Source0 只包含了一个回调(函数指针),它并不能主动触发事件。...这个事件是怎么执行的?并且为什么有的时候会延迟?为什么子线程中创建的Timer并不执行?...我们看到runLoop中执行任务有调到CFRunLoopDoBlocks这么一个函数,那么这个函数是什么样的呢?
RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数来执行事件循环的逻辑。...线程执行了这个函数后,就会一直处于这个函数内部 “接受消息->等待->处理” 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。...CFRunLoop 是基于 pthread 来管理的。...Source有两个版本:Source0 和 Source1:Source0 只包含了一个回调(函数指针),它并不能主动触发事件。...其包含一个时间长度和一个回调(函数指针)。当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调。
通过将那些 繁重而不紧急会大量占用 CPU 的任务(比如图片加载),放到空闲的 RunLoop 模式里执行,这 样就可以避开在 UITrackingRunLoopMode 这个 RunLoop 模式时是执行...接下来,我就通过 CFRunLoop 的源码来跟你分享下 RunLoop 的原理吧。...通知 Observers: RunLoop 即将触发 Source0 (非port) 回调。...RunLoop 触发 Source0 (非port) 回调。..._PERFORM_FUNCTION(source0)和CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION(source1)之前。
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象 CFRunLoopGetMain(); // 获得主线程的RunLoop对象 RunLoop与线程的关系 线程和 RunLoop 之间是一一对应的 CFRunLoop...CFRunLoopSourceRef 是事件产生的地方 Source0 只包含了一个回调(函数指针),它并不能主动触发事件。...当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间点到时,RunLoop会被唤醒以执行那个回调 3. CFRunLoopObserverRef 是观察者。...RunLoop 这个对象,在 iOS 里由CFRunLoop 实现。简单来说,RunLoop 是用来监听输入源,进行调度处理的。这里的输入源可以是输入设备、网络、周期性或者延迟时间、异步回调。...input source来和其他线程进行通信 2.在线程(非主线程)中使用timer 3.使用 performSelector…系列(如performSelectorOnThread, …) 4.使用线程执行周期性工作
领取专属 10元无门槛券
手把手带您无忧上云