[iOS面试]第7章 RunLoop相关面试问题

2020-05-11  本文已影响0人  codeTao

本文主讲RunLoop相关面试问题,包括RunLoop概念、数据结构、事件循环机制、RunLoop与NSTimer、RunLoop与多线程。

一、RunLoop概念

1、 问题:什么是RunLoop?

答:RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象。
1、没有消息需要处理时,休眠以避免资源占有。
2、有消息需要处理时,立刻被唤醒。

2、EventLoop

没有消息需要被处理时, 系统会将当前线程所有权转化为内核态, 当有消息需要处理时, 系统会将当前线程的状态切换回用户态.
所以RunLoop的循环并不是一个单纯的死循环, 而是通过状态切换, 达到没有消息时休眠, 有消息时唤醒的这样一个事件循环机制.

EventLoop
在没有消息处理时,休眠以避免资源占用,它的状态切换是怎么样的?
用户态和内核态的介绍:
问题:main函数为什么能保证一直运行状态不退出?

答:

什么是事件循环,事件循环的机制是怎样的?

二、RunLoop的数据结构

RunLoop开源代码地址
在 OC 中实际提供了两个 RunLoop 的。
一个是 NSRunLoop,一个是 CFRunLoop。
NSRunLoop 是对 CFRunLoop 的封装,提供了一些面向对象的 API。
NSRunLoop 是位于 Foundation 当中的,CFRunLoop 位于 CoreFoundation 当中的。

RunLoop 的数据结构主要有三个:

1、CFRunLoop

//源码
struct __CFRunLoop {
    CFMutableSetRef _commonModes;     // Set
    CFMutableSetRef _commonModeItems; // Set
    CFRunLoopModeRef _currentMode;    // Current Runloop Mode
    CFMutableSetRef _modes;           // Set
    ...
};

2、CFRunLoopMode数据结构

CFRunLoopMode数据结构
//源码
struct __CFRunLoopMode {
    CFStringRef _name;            // Mode Name, 例如 @"kCFRunLoopDefaultMode"
    CFMutableSetRef _sources0;    // Set
    CFMutableSetRef _sources1;    // Set
    CFMutableArrayRef _observers; // Array
    CFMutableArrayRef _timers;    // Array
    ...
};

3、Source/Timer/Observer

1>、CFRunLoopSource

source0:需要手动唤醒线程。
source1:具备唤醒线程的能力。

source0: 非系统事件.
只包含了一个回调(函数指针),它并不能主动触发事件。使用时,你需要先调用 CFRunLoopSourceSignal(source),将这个 Source 标记为待处理,然后手动调用 CFRunLoopWakeUp(runloop) 来唤醒 RunLoop,让其处理这个事件。

source1 : 系统事件
包含了一个 mach_port和一个回调(函数指针),被用于通过内核和其他线程相互发送消息。这种 Source 能主动唤醒 RunLoop 的线程

2>、CFRunLoopTimer

基于事件的定时器,和NSTimer是toll-free bridged的。和平时所使用的 NSTimer 是具备免费桥转换的

3>、CFRunLoopObserver

某个observer可以监听runloop的状态变化,并作出一定反应。

观测时间点

可以通过注册一些 Observer 来实现对 RunLoop 的一些相关时间点的监测或者观察。

4、各个数据结构之间关系

数据结构之间关系

问题:RunLoop 和Mode以及 Mode和其对应的 Source ,Timer , Observer 有什么关系?
答:

5、RunLoop的Mode

image.png

Runloop Mode 实际上是 Source,Timer 和 Observer 的集合,不同的 Mode 把不同组的 Source,Timer 和 Observer 隔绝开来。Runloop 在某个时刻只能跑在一个 Mode 下,处理这一个 Mode 当中的 Source,Timer 和 Observer。

苹果文档中提到的 Mode 有五个,分别是:

iOS 中公开暴露出来的只有 NSDefaultRunLoopModeNSRunLoopCommonModes。 NSRunLoopCommonModes 实际上是一个 Mode 的集合,默认包括 NSDefaultRunLoopMode 和 NSEventTrackingRunLoopMode。

问题:RunLoop为什么会有多个mode?
答:

6、CommonModes的特殊性

NSRunLoopCommonModes
(问题:CommonModes是否使用过, 你对CommonModes事怎样理解?)

在 OC 当中经常会通过 NSRunLoopCommonModes 字符串常量来表达 CommonMode。

三、RunLoop事件循环机制的实现

在开发过程中调用的 NSRunLoop 的 run 系列的相关方法以及 CFRunLoop 的相关的 run 方法最终都会调用到 CFRunLoopRun() 函数

1 事件循环的整体逻辑:

1)在 RunLoop 启动之后首先会发出一条通知来告诉观察者当前 RunLoop 即将启动
2)之后 RunLoop 将要处理 Timer/Sources0 事件,发出通知
3)进入正式 Sources0 的处理
4)如果有 Sources1 需要处理,这个时候会用过一条 goto 语句来进行代码逻辑的跳转,来处理唤醒时收到的消息
5)如果没有 Source1要处理,此时线程将要休眠,同时也会发送通知给 Observe,然后就发生了用户态到内核态的切换
6)线程正式进入休眠,等待唤醒
7)之后线程被唤醒,也要发送一个通知,通知观察着说当前线程被唤醒了,然后处理唤醒时受到的消息,之后又会回到第二步

2 当处于休眠的runloop,可以通过哪些方式唤醒?

答:

3 App 从启动到退出,这个过程当中系统都发生了什么?

答:

4 RunLoop的核心

main 函数经过一系列的处理之后,内部最终会调用一个系统函数 mach_msg() ,于是就发生了一个系统调用,经过系统调用当前用户线程就把控制权转交核心态,然后 mach_msg() 在一定条件下会返回给调用方,触发返回的逻辑就是唤醒线程的逻辑,比如收到了一个 Sources1 或者 Timer 事件的回调,包括外部手动唤醒,就可以触发核心态到用户态的切换,那么当前app的主线程循环就会被唤醒,这就是 RunLoop 的核心

四、RunLoop与NSTimer

问题:滑动TableView的时候,我们的定时器还会生效吗?
答:

void CFRunLoopAddTimer(runloop, timer, commonMode)

五、RunLoop与多线程

1、怎样实现一个常驻线程?

(1)、为当前线程开启一个RunLoop。
(2)、向该RunLoop中添加一个Port/Source等维持RunLoop的事件循环。
(3)、启动该RunLoop。

实现一个常驻线程的基本步骤:

2、RunLoop和线程关系

(1)、runloop与线程是一一对应的,一个runloop对应一个核心的线程,为什么说是核心的,是因为runloop是可以嵌套的,但是核心的只能有一个,他们的关系保存在一个全局的字典里。
(2)、runloop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休眠状态,有了任务就会被唤醒去执行任务。
(3)、runloop在第一次获取时被创建,在线程结束时被销毁。
(4)、对于主线程来说,runloop在程序一启动就默认创建好了。
(5)、对于子线程来说,runloop是懒加载的,只有当我们使用的时候才会创建。

    dispatch_queue_t queue = dispatch_queue_create("com.codeTao.testQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        [self performSelector:@selector(timerAction) withObject:nil afterDelay:1];
        [[NSRunLoop currentRunLoop] run];
    });

(6)、子线程中使用定时器,需将定时器添加至RunLoop中,确保子线程的runloop被创建,不然定时器不会回调。

  dispatch_queue_t queue = dispatch_queue_create("com.codeTao.testQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        //此种方式创建的timer已经添加到NSRunloop中了
        NSTimer *timer1 =[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] run];

        //此种方式创建的timer没有添加至runloop中
        NSTimer *timer2 = [NSTimer timerWithTimeInterval:1.0f target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer2 forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    });

六 RunLoop面试总结

问题:什么是RunLoop? 它是怎样做到有事做事,没事休息?

答: RunLoop是通过内部维护的事件循环来对事件/消息进行管理的一个对象。
调用 [CFRunLoop run] 相关方法后, 会调用系统函数mach_msg, 同时发生了用户态向核心态的切换,当前线程处于休眠状态.所有做到有事做事,没事休息

问题:RunLoop与线程是怎样的关系?

答:

问题:如何实现一个常驻线程?

答:三个步骤:
(1)、创建一个线程对应的RunLoop。
(2)、向该RunLoop中添加一个Port/Source/Timer/Observer等维持RunLoop的事件循环。
(3)、启动该RunLoop 。调用CFRunLoop run方法
注意:运行的模式和添加模式必须是同一个,否则外部使用while循环会导致死循环

问题:怎样保证子线程数据回来更新UI的时候不打断用户的滑动操作?

答:

问题:我们可以监听 RunLoop 哪些时间点?

答:


问题:什么时候使用Runloop?
当需要和该线程进行交互的时候才会使用Runloop

问题: Runloop和线程是什么关系?
答:

问题:Runloop的mode作用是什么?
答:
指定事件在运行循环中的优先级的,
线程的运行需要不同的模式,去响应各种不同的事件,去处理不同情境模式。(比如可以优化tableview的时候可以设置UITrackingRunLoopMode下不进行一些操作,比如设置图片等。)

问题: 以+scheduledTimerWithTimeInterval:的方式触发的timer,在滑动页面上的列表时,timer会暂停回调, 为什么?
答:
滑动scrollView时,主线程的RunLoop会切换到UITrackingRunLoopMode这个Mode,执行的也是UITrackingRunLoopMode下的任务(Mode中的item),而timer是添加在NSDefaultRunLoopMode下的,所以timer任务并不会执行,只有当UITrackingRunLoopMode的任务执行完毕,runloop切换到NSDefaultRunLoopMode后,才会继续执行timer。

问题: 如何解决在滑动页面上的列表时,timer会暂停回调?
答:
Timer放到NSRunLoopCommonModes中执行即可

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];

问题: NSTimer使用时需要注意什么?
答:

问题: RunLoop 有哪些应用?
答: 常驻内存、AutoreleasePool 自动释放池

问题: AutoreleasePool 和 RunLoop 有什么联系?
答:
iOS应用启动后会注册两个 Observer 管理和维护 AutoreleasePool。应用程序刚刚启动时默认注册了很多个Observer,其中有两个Observer的 callout 都是 _ wrapRunLoopWithAutoreleasePoolHandler,这两个是和自动释放池相关的两个监听。

问题:NSRunLoop 和 CFRunLoopRef 区别?
答: CFRunLoopRef 基于C 线程安全,NSRunLoop 基于 CFRunLoopRef 面向对象的API 是不安全的

上一篇 下一篇

猜你喜欢

热点阅读