由于各种原因,需要对RunLoop进行研究,通过阅读大神的文章对RunLoop也有了一些了解,在这里进行下小结。
RunLoop的概念
首先说下Event Loop模型。一般来讲,一个线程一次只能执行一个任务,执行完成后线程就会退出。让线程能随时处理事件但并不退出,这种机制就是Event Loop。在iOS中的RunLoop就是这个模型的应用。RunLoop用来管理事件/消息,如何让线程在没有处理消息的时候休眠以避免资源占用,在消息到来时立刻被唤醒。RunLoop通过提供的入口函数来执行上面的EventLoop的逻辑。线程执行了这个函数后,就会一直处于这个函数内部"接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。
RunLoop与线程的关系
线程和RunLoop是一一对应的,即每个线程都有个对应的RunLoop。它们是键值对关系,保持在一个全局字典里。RunLoop不可以直接创建,但是可以通过CFRunLoopGetMain() 和 CFRunLoopGetCurrent()两个自动获取函数获取。调用CFRunLoopGetMain()方法,第一次进来时,系统会初始化全局的字典,并先为主线程创建一个RunLoop 。然后从字典里获取loop然后返回。调用CFRunLoopGetCurrent()方法时会直接从字典里获取,如果获取不到,系统会地动创建一个,同时注册一个回调,当线程销毁时,顺便销毁其对应的RunLoop。然后返回loop。总的来说,线程刚创建的时候是没有RunLoop,只有自动去获取才会创建,创建后会在线程结束时销毁。你只能在一个线程的内部获取其RunLoop(主线程除外)。
RunLoop的对外接口和内部结构
在 CoreFoundation 里面关于 RunLoop 有5个类:
CFRunLoopRef、CFRunLoopModeRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObserverRef
他们的关系如下:
一个 RunLoop 包含若干个 Mode,每个 Mode 又包含若干个 Source/Timer/Observer。每次调用 RunLoop 的主函数时,只能指定其中一个 Mode,这个Mode被称作 CurrentMode。如果需要切换 Mode,只能退出 Loop,再重新指定一个 Mode 进入。这样做主要是为了分隔开不同组的 Source/Timer/Observer,让其互不影响。
RunLoop的Mode有5个成员,Mode的name、Source0、Source1、Observers、Timer。
RunLoop内部逻辑:
Observer监听事件,回调需要处理的事件。其中有三种方式的回调,通过Source0回调,通过Timer定时器回调,外部手动唤醒。Source0不能主动触发事件,需要把它标记为待处理,然后手动唤醒RunLoop。Source1与之相反(Source1能主动唤醒RunLoop的线程,被用于通过内核和其他线程相互发消息)。
Timer(CFRunLoopTimerRef)时间触发器,其包含一个时间长度和一个回调(函数指针)。当其加入到RunLoop时,RunLoop会注册对应的世界点,当时间点到时,RunLoop会被唤醒以执行这个回调。
观察者(CFRunLoopObserverRef)Observer,每个Observer都包含一个回调,当RunLoop状态发生变化时,观察者就能通过回调接受到这个变化。时间点有6个:即将进入RunLoop、即将处理Timer、即将处理Source、即进入休眠,刚从休眠中唤醒,即将推出RunLoop。
具体流程:
首先根据ModeName找到对应的mode,如果mode里没有source、timer、obsever就直接返回。1.通知Observer即将进入loop。2.通知Observer即将触发Source0回调。4.触发Source0的回调。5.如果有Source1,直接处理然后跳转去处理消息。6.通知Observer即将进入休眠。7.线程进入休眠,直到被唤醒。8.通知Observer线程被唤醒了。9.收到消息,处理消息。10.通知Observer即将退出RunLoop。
RunLoop在iOS中的应用
这里只是列举出应用:
1.自动释放池(AutoreleasePool) 2.事件响应 3.手势识别 4.界面更新 5.定时器 6.网络请求(AFNetworking)。
总结
大部分都是借鉴这位大神的话,因为自己对RunLoop的理解也只是在字面上。基本上也明白了RunLoop是什么。有错误欢迎大家指出!