Runloop 机制

1.什么是runLoop

    :一般来讲,一个线程一次只能执行一个任务执行完之后线程就会退出,runloop保障线程随时处理事件但不退出 ,RunLoop 实际上就是一个对象,这个对象管理了其需要处理的事件和消息,并提供了一个入口函数,线程执行了这个函数后,就会一直处于这个函数内部 "接受消息->等待->处理" 的循环中,直到这个循环结束(比如传入 quit 的消息),函数返回。

2.runloop与线程的关系

 苹果不允许直接创建Runloop,但是可以使用CFRunLoopGetMain() 和 CFRunLoopGetCurrent()函数获取runloop,在每一个线程中首次调用CFRunLoopGetCurrent()函数系统会帮我们创建好runloop对象并返回,主线程的runloop是默认开启的,子线程中默认未创建,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)

3.解析runloop

CFRunLoopRef   runloop对象

CFRunLoopModeRef  runloop模式

CFRunLoopSourceRef 事件

CFRunLoopTimerRef   计时器事件

扫描二维码关注公众号,回复: 4050268 查看本文章

CFRunLoopObserverRef 观察者

3.1Runloop的mode

struct __CFRunLoop {

    CFMutableSetRef _commonModes;     // Set

    CFMutableSetRef _commonModeItems; // Set<Source/Observer/Timer>

    CFRunLoopModeRef _currentMode;    // Current Runloop Mode

    CFMutableSetRef _modes;           // Set

    ...

};

struct __CFRunLoopMode {

    CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"

    CFMutableSetRef _sources0;    // Set

    CFMutableSetRef _sources1;    // Set

    CFMutableArrayRef _observers; // Array

    CFMutableArrayRef _timers;    // Array

    ...

};

Source0:处理App内部事件,如UIEvent、CFSocket,对应(CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION)这个函数。

Source1:由RunLoop和内核管理,Mach port驱动,如CFMachPort、CFMessagePort。对应(CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION)这个函数。

Observers:主要负责修改RunLoop的状态

Timers:负责让App响应NSTimers,延迟的perform事件,对应(CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION)这个函数

每当 RunLoop 的内容发生变化时,RunLoop 都会自动将 _commonModeItems 里的 Source/Observer/Timer 同步到具有 "Common" 标记的所有Mode里。

应用场景举例:主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。这两个 Mode 都已经被标记为"Common"属性。DefaultMode 是 App 平时所处的状态,TrackingRunLoopMode 是追踪 ScrollView 滑动时的状态。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个TableView时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。

有时你需要一个 Timer,在两个 Mode 中都能得到回调,一种办法就是将这个 Timer 分别加入这两个 Mode。还有一种方式,就是将 Timer 加入到顶层的 RunLoop 的 "commonModeItems" 中。"commonModeItems" 被 RunLoop 自动更新到所有具有"Common"属性的 Mode 里去。

runloop只能后增加runloopmode,不可以删除mode ,有关mode的操作

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);

一个run loop模式是一个将要被监听额输入源和定时器的集合,以及等待run loop通知的观察者集合。你每次启动run loop,你显示或者隐士的指定一个“模式”来运行,在run loop的运行过程中,只有和指定模式相关的源才会被监听和分发它们的事件(相似的,只有和指定模式关联的观察者才能获得run loop运行进度的通知),和其他模式相关的输入源会将任何接收到的事件保存起来,直到后来以合适的模式运行run loop。

3.2runloop内部逻辑

runloop函数调用顺序 

其中__CFRunLoopRun 函数内部是一个do while循环 while的判断语句是表示runloop运行状态的枚举值CFRunLoopRunResult 当枚举值为0的时候函数返回

3.3RunLoop底层实现

RunLoop 的核心是基于 mach port 的,RunLoop 的核心就是一个 mach_msg() ,RunLoop 调用这个函数去接收消息,如果没有别人发送 port 消息过来,内核会将线程置于等待状态(休眠)。例如你在模拟器里跑起一个 iOS 的 App,然后在 App 静止时点击暂停,你会看到主线程调用栈是停留在 mach_msg_trap() 

3.4系统提供五种runloop

    UITrackingRunLoopMode,滑动追踪

    GSEventReceiveRunLoopMode,接受系统事件的内部 Mode

    kCFRunLoopDefaultMode,默认

    UIInitializationRunLoopMode,初始化

    kCFRunLoopCommonModes

3.5显式开启runloop

Use ports or custom input sources to communicate with other threads.

Use timers on the thread.

Use any of the performSelector… methods in a Cocoa application.

Keep the thread around to perform periodic tasks

 1.使用端口或者自定义的输入源和其他线程通信

 2.在线程上使用定时器

 3.在cocoa应用中使用任意一个performSelector…方法做延迟操作

 4.使得线程不被杀死去做周期性任务

  

3.6 runloop退出

runloop退出的条件:app退出;线程关闭;设置最大时间到期;modeItem为空  

猜你喜欢

转载自blog.csdn.net/qq_28117621/article/details/83994474