首页 >> 大全

RunLoop原理及应用

2023-12-05 大全 29 作者:考证青年

原理 一个形象的比喻来说明

进程是一家工厂,线程是一个流水线,Run Loop就是流水线上的主管;当工厂接到商家的订单分配给这个流水线时,Run Loop就启动这个流水线,让流水线动起来,生产产品;当产品生产完毕时,Run Loop就会暂时停下流水线,节约资源。

管理流水线,流水线才不会因为无所事事被工厂销毁;而不需要流水线时,就会辞退这个主管,即退出线程,把所有资源释放。

并不是iOS平台的专属概念,在任何平台的多线程编程中,为控制线程的生命周期,接收处理异步消息都需要类似的循环机制实现,的就是类似的机制。

定义

当有持续的异步任务需求时,我们会创建一个独立的生命周期可控的线程。就是控制线程生命周期并接收事件进行处理的机制。

是iOS事件响应与任务处理最核心的机制,它贯穿iOS整个系统。

特性 对象

    // Foundation[NSRunLoop mainRunLoop];     // 获取主线程的 RunLoop 对象[NSRunLoop currentRunLoop];  // 获取当前线程的 RunLoop 对象// Core FoundationCFRunLoopGetMain();     // 获取主线程的 RunLoop 对象CFRunLoopGetCurrent();  // 获取当前线程的 RunLoop 对象

关键代码解析

1.通知 : 要开始进入 loop 了。紧接着就进入 loop。代码如下:


//通知 observers
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
//进入 loop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);

2.开启一个 do while 来保活线程。通知 : 会触发 Timer 回调、 回调,接着执行加入的 block。代码如下:


// 通知 Observers RunLoop 会触发 Timer 回调
if (currentMode->_observerMask & kCFRunLoopBeforeTimers)__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
// 通知 Observers RunLoop 会触发 Source0 回调
if (currentMode->_observerMask & kCFRunLoopBeforeSources)__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
// 执行 block
__CFRunLoopDoBlocks(runloop, currentMode);

接下来,触发 回调,如果有 是 ready 状态的话,就会跳转到 去处理消息。代码如下


if (MACH_PORT_NULL != dispatchPort ) {Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)if (hasMsg) goto handle_msg;
}

3.回调触发后,通知 : 的线程将进入休眠(sleep)状态。代码如下:


Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
if (!poll && (currentMode->_observerMask & kCFRunLoopBeforeWaiting)) {__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
}

4.进入休眠后,会等待 的消息,以再次唤醒

只有在下面四个事件出现时才会被再次唤醒:

等待唤醒的代码:


do {__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {// 基于 port 的 Source 事件、调用者唤醒if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {break;}// Timer 时间到、RunLoop 超时if (currentMode->_timerFired) {break;}
} while (1);

唤醒时通知 : 的线程刚刚被唤醒了。代码如下:

应用原理还是运用原理__应用原理和理论依据怎么写


if (!poll && (currentMode->_observerMask & kCFRunLoopAfterWaiting))__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);

5. 被唤醒后就要开始处理消息了:如果是 Timer 时间到的话,就触发 Timer 的回调;如果是 的话,就执行 block;如果是 事件的话,就处理这个事件。消息执行完后,就执行加到 loop 里的 block。代码如下


handle_msg:
// 如果 Timer 时间到,就触发 Timer 回调
if (msg-is-timer) {__CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
} 
// 如果 dispatch 就执行 block
else if (msg_is_dispatch) {__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} // Source1 事件的话,就处理这个事件
else {CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);if (sourceHandledThisLoop) {mach_msg(reply, MACH_SEND_MSG, reply);}
}

6.根据当前 的状态来判断是否需要走下一个 loop。当被外部强制停止或 loop 超时时,就不继续下一个 loop 了,否则继续走下一个 loop 。代码如下:


if (sourceHandledThisLoop && stopAfterHandle) {// 事件已处理完retVal = kCFRunLoopRunHandledSource;
} else if (timeout) {// 超时retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(runloop)) {// 外部调用者强制停止retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {// mode 为空,RunLoop 结束retVal = kCFRunLoopRunFinished;
}

处理流程图如下:

机制

主线程 (有 的线程) 几乎所有函数都从以下六个之一的函数调起:

用于向外部报告 当前状态的更改,框架中很多机制都由 触发,如

消息通知、非延迟的、调用、block回调、KVO

延迟的, 延迟调用,定时器

由和内核管理,Mach port驱动,如、

输入源 按照同步异步分类

按照类型分类

触摸事件、::

基于Port的线程间的通信,系统事件的捕捉.

(两个线程之间相互传递消息的处理,系统事件捕捉,其实也包括触摸事件,只是把事件捕捉到以后传递给).

Timer

定时器,::(这个方法的底层实现也就是来实现的)

用于监听的状态,UI的刷新(), pool()

(在休眠之前都会去执行UI的刷新啊、 pool的释放等)

在启动之前,必须添加监听的输入源事件或者定时源事件,否则调用[ run]会直接返回,而不会进入循环让线程长驻。

如果没有添加任何输入源事件或Timer事件,线程会一直在无限循环空转中,会一直占用CPU时间片,没有实现资源的合理分配。

没有while循环且没有添加任何输入源或Timer的线程,线程会直接完成,被系统回收。

//错误做法
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
while (!self.isCancelled && !self.isFinished) {[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
};
//正确做法
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
while (!self.isCancelled && !self.isFinished) {@autoreleasepool {[runLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];}
}

的运行模式

的运行模式共有5种,只会运行在一个模式下,要切换模式,就要暂停当前模式,重写启动一个运行模式。每一种事件源添加进的时候

- kCFRunLoopDefaultMode, App的默认运行模式,通常主线程是在这个运行模式下运行
- UITrackingRunLoopMode, 跟踪用户交互事件(用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他Mode影响)
- kCFRunLoopCommonModes, 伪模式,不是一种真正的运行模式
- UIInitializationRunLoopMode:在刚启动App时第进入的第一个Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode:接受系统内部事件,通常用不到

有很多种模式,对应的只有一种.

1.它是代表的运行模式

2.一个包含若干个Mode,每个Mode又包含若干个//Timer/

3.启动时只能选择其中一个Mode,作为

4.如果需要切换Mode,只能退出当前,再重新选择一个Mode进入

5.不同组的//Timer/能分割开来,互不影响

6.如果Mode里面没有任何//Timer/,会立马退出

2.和区别:

不能主动触发事件。只有一个回调(函数指针),使用时先调用l ()将标记为待处理,然后调用 ()唤醒,让处理事件。

能主动唤醒的线程。有一个和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。

的六个状态


typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {kCFRunLoopEntry , // 进入 loopkCFRunLoopBeforeTimers , // 触发 Timer 回调kCFRunLoopBeforeSources , // 触发 Source0 回调kCFRunLoopBeforeWaiting , // 等待 mach_port 消息kCFRunLoopAfterWaiting ), // 接收 mach_port 消息kCFRunLoopExit , // 退出 loopkCFRunLoopAllActivities  // loop 所有状态改变
}

的应用

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了