IOS面试题——OC中Runloop原理与使用(9)

一 面试题汇总

  1. 什么是Runloop?有什么作用?常用来做什么?
  2. Runloop与线程之间的关系?
  3. Runloop在内存中如何存储?key是线程
  4. Runloop相关的类有哪些?
  5. CFRunLoopModeRef是什么?有哪几种mode?
  6. Source0/Source1/Timer/Observer是什么,与mode有什么关系?
  7. CFRunLoopObserverRef包含哪几种状态?
  8. 如何监听RunLoop的所有状态?
  9. Runloop具体流程?
  10. 用户态和内核态是什么?
  11. 线程保活怎么做?

二 面试题解答(仅供参考)

2.1 什么是Runloop?有什么作用?常用来做什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Runloop(运行循环)是 iOS 和 macOS 中的一个重要的系统框架,
用于管理事件响应、定时器、输入源和其他相关任务的调度和执行。
它是一个事件处理的循环结构,持续地监听事件并处理事件,直到没有事件需要处理或者手动终止。

Runloop 的主要作用包括:

1-事件处理:Runloop 负责监听和处理事件,包括用户输入、定时器事件、系统消息等。
当事件发生时,Runloop 会根据事件类型调用相应的事件处理函数来处理事件。

2-线程管理:每个线程都有一个与之对应的 Runloop,用于管理该线程的事件循环和任务执行。
Runloop 会在线程启动时自动创建,并在线程结束时自动释放。

3-资源管理:Runloop 可以帮助管理线程的资源使用,包括线程的生命周期、内存管理、资源回收等。
它可以确保线程在不需要执行任务时保持休眠状态,节省资源。

4-性能优化:通过合理地配置和使用 Runloop,可以提高应用程序的性能和响应速度。
例如,通过合理设置定时器和事件源,可以减少不必要的计算和资源消耗,提高应用程序的效率。

Runloop 在 iOS 和 macOS 开发中常用来做以下几件事情:

1-UI 更新:在 iOS 应用中,Runloop 负责处理用户界面的刷新和响应用户的交互事件。
2-定时器:Runloop 可以用来创建和管理定时器,定时执行特定的任务或代码块。
3-网络请求:在网络编程中,Runloop 可以用来监听和处理网络事件,例如接收数据、发送数据等。
4-文件 I/O:Runloop 可以用来监听和处理文件 I/O 事件,例如文件读取、写入等。
5-事件分发:Runloop 可以用来监听和处理系统消息,例如系统通知、系统状态改变等。

总的来说,Runloop 是 iOS 和 macOS 开发中一个重要的系统框架,
它负责管理线程的事件循环和任务执行,帮助开发者编写高效、响应迅速的应用程序。

2.2 Runloop与线程之间的关系?

1
2
3
4
Runloop 与线程之间是一种一对一的关系,
每个线程都有一个与之对应的 Runloop 用于管理该线程的事件循环和任务执行。
Runloop 在线程休眠时保持活跃状态,提供了一个事件循环机制,
用于监听和处理事件,保证了线程的活跃性和响应性。

2.3 Runloop在内存中如何存储?key是线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Runloop 在内存中的存储通常由系统维护,具体实现会依赖于底层的操作系统和运行时环境。
一般来说,每个线程都会有一个对应的 Runloop 对象,
而 Runloop 对象中会包含一些与线程事件循环相关的信息和数据结构。

在 macOS 和 iOS 中,Runloop 通常由 Core Foundation 框架提供的 CFRunLoopRef 类型来表示。
CFRunLoopRef 对象实际上是一个指向内存中存储 Runloop 信息的结构体的指针。
这个结构体中包含了线程的 Runloop 状态、事件源(Sources)、定时器(Timers)、观察者(Observers)等相关信息。

关于线程的 Runloop 信息在内存中的存储通常是由系统进行管理和维护的,开发者无法直接访问和修改。
而对于开发者而言,可以通过 Core Foundation 和 Foundation 框架提供的函数和类来操作 Runloop,
如创建 Runloop、添加事件源、定时器和观察者等。

总的来说,Runloop 在内存中的存储由系统进行管理和维护,
具体实现依赖于底层的操作系统和运行时环境,开发者可以通过相关的框架和接口来操作和管理 Runloop。

2.4 Runloop相关的类有哪些?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
在 Objective-C 中,与 Runloop 相关的类主要来自于 Core Foundation 框架和 Foundation 框架。
以下是一些常用的与 Runloop 相关的类:

1-Core Foundation 框架:

CFRunLoopRef:代表了一个 Runloop 对象,用于管理线程的事件循环和任务执行。
CFRunLoopSourceRef:代表了一个事件源,用于向 Runloop 添加事件。
CFRunLoopTimerRef:代表了一个定时器,用于向 Runloop 添加定时任务。
CFRunLoopObserverRef:代表了一个观察者,用于监视 Runloop 的状态变化。

2-Foundation 框架:

NSRunLoop:Objective-C 对 CFRunLoopRef 的封装,提供了更方便的 Objective-C 接口。
NSRunLoopMode:定义了 Runloop 的模式,用于区分不同的事件源和定时器。
NSTimer:用于创建定时器对象,可以添加到 Runloop 中进行定时任务的调度。
NSPort 和 NSMachPort:用于创建端口对象,可以通过端口与其他线程或进程进行通信,
并将端口添加到 Runloop 中监听事件。

这些类提供了在 Objective-C 中操作 Runloop 的相关接口和功能,
开发者可以通过这些类来创建、配置和管理 Runloop,添加事件源和定时器,监视 Runloop 的状态变化等。通过这些类,开发者可以更方便地使用 Runloop 来管理线程的事件循环和任务执行。

2.5 CFRunLoopModeRef是什么?有哪几种mode?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
CFRunLoopModeRef 是 Core Foundation 框架中表示 Runloop 模式的数据类型。
Runloop 模式是用来区分不同类型的事件源和定时器,以便在 Runloop 中进行事件分发和调度。
每个 Runloop 都包含多个 Runloop 模式,每个模式都可以包含多个事件源和定时器。

常见的 Runloop 模式包括:

1-Default Mode(默认模式):

默认模式是 Runloop 的默认模式,当 Runloop 启动时,会自动进入默认模式。
大多数情况下,事件都是在默认模式下处理的。主线程的 Runloop 默认包含了默认模式。

2-Common Modes(公共模式):

公共模式是一组预定义的常用模式,包含了一些公共事件源和定时器。
这些模式通常用于处理一些常见的任务,如网络请求、UI 更新等。
常见的公共模式包括 NSDefaultRunLoopMode、NSRunLoopCommonModes。

3-其他自定义模式:

除了默认模式和公共模式之外,开发者还可以创建自定义的 Runloop 模式,用于处理特定类型的任务。
自定义模式可以根据需要添加事件源和定时器,以实现特定的业务逻辑。

每个 Runloop 模式都有一个名称,用于标识模式的类型。
在事件分发过程中,Runloop 会根据当前所处的模式来选择需要处理的事件源和定时器。
开发者可以根据需要将事件源和定时器添加到特定的模式中,以实现不同类型的事件分发和调度。

2.6 Source0/Source1/Timer/Observer是什么,与mode有什么关系?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
在 Runloop 中,Source0、Source1、Timer 和 Observer 是四种不同类型的事件源,
它们用于向 Runloop 添加事件,从而触发相应的处理逻辑。
这些事件源与 Runloop 模式之间存在一定的关系,不同类型的事件源可以被添加到不同的 Runloop 模式中,
以便在特定模式下处理特定类型的事件。

1-Source0(基于端口的事件源):

Source0 是基于端口的事件源,通常用于线程间的通信或者与系统底层的事件源进行交互。
它们通过接收端口消息来触发事件处理。
Source0 不会被 Runloop 自动处理,需要手动触发事件处理。
例如,使用 CFRunLoopSourceSignal() 函数来手动触发事件处理。

2-Source1(基于系统事件的事件源):

Source1 是基于系统事件的事件源,用于处理系统级别的事件,如触摸事件、定时器事件等。
它们会被 Runloop 自动触发,无需手动处理。
Source1 可以被添加到 Runloop 的特定模式中,以便在特定模式下处理特定类型的事件。

3-Timer(定时器):

定时器是一种周期性触发的事件源,用于定时执行任务。
在 Runloop 中,定时器会被添加到特定的 Runloop 模式中,以便在该模式下定时触发事件处理。

4-Observer(观察者):

观察者是用于监视 Runloop 状态变化的事件源,可以监听 Runloop 的启动、结束、休眠、唤醒等状态变化。
它们可以在特定的 Runloop 模式中被添加,以便在模式发生变化时触发相应的处理逻辑。

与 Runloop 模式的关系:

1-每个事件源(Source0、Source1、Timer、Observer)都可以被添加到特定的 Runloop 模式中。
2-当 Runloop 进入某个模式时,它会检查该模式下是否有对应类型的事件源,如果有则会触发相应的事件处理。
3-开发者可以根据需要将不同类型的事件源添加到不同的模式中,以实现特定类型事件的处理。

2.7 CFRunLoopObserverRef包含哪几种状态?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
CFRunLoopObserverRef 是 Core Foundation 框架中表示 Runloop 观察者的数据类型,
用于监视 Runloop 的状态变化。
CFRunLoopObserverRef 可以监视 Runloop 的多种状态,其中包括以下几种主要状态:

1-Entry(进入):

当 Runloop 即将进入运行循环时,会触发 Entry 状态的回调函数。
此时 Runloop 尚未开始处理事件,处于准备状态。

2-BeforeTimers(定时器触发前):

在 Runloop 处理定时器事件之前,会触发 BeforeTimers 状态的回调函数。
此时 Runloop 即将开始处理定时器事件。

3-BeforeSources(源事件触发前):

在 Runloop 处理源事件之前,会触发 BeforeSources 状态的回调函数。
此时 Runloop 即将开始处理源事件。

4-BeforeWaiting(即将进入休眠):

当 Runloop 即将进入休眠状态时,会触发 BeforeWaiting 状态的回调函数。
此时 Runloop 即将进入休眠,等待事件的到来。

5-AfterWaiting(唤醒后):

在 Runloop 从休眠状态唤醒后,会触发 AfterWaiting 状态的回调函数。
此时 Runloop 已经从休眠状态中恢复,并开始处理事件。

6-Exit(退出):

当 Runloop 即将退出运行循环时,会触发 Exit 状态的回调函数。
此时 Runloop 即将结束运行循环,准备退出。

通过监听这些状态,开发者可以在不同阶段插入自定义的处理逻辑,从而实现更加精细的 Runloop 监控和管理

2.8 如何监听RunLoop的所有状态?

1
2
3
4
5
6
7
8
要监听 RunLoop 的所有状态,您需要使用 CFRunLoopObserver。
CFRunLoopObserver 是 Core Foundation 框架中的一个类,用于监视 RunLoop 的状态改变。
您可以创建一个 CFRunLoopObserver 并将其添加到 RunLoop 中,以便在 RunLoop 的不同状态下接收通知。
以下是如何监听 RunLoop 的所有状态的一般步骤:

1-创建 CFRunLoopObserver。
2-设置观察者的回调函数,该函数会在 RunLoop 的状态改变时被调用。
3-将观察者添加到 RunLoop 中。

2.9 Runloop具体流程?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
RunLoop 是 iOS 和 macOS 系统中的一个重要概念,它用于处理事件、定时器和输入源等任务,
并且在应用程序的主线程中起着至关重要的作用。
RunLoop 的具体流程如下:

1-获取RunLoop对象:
在 iOS 和 macOS 开发中,通常不直接创建 RunLoop 对象,
而是通过NSRunLoop类(Objective-C)或RunLoop类(Swift)获取当前线程的RunLoop对象。

2-进入循环:
一旦获取了RunLoop对象,主线程会进入一个无限循环,不断地处理各种事件,
这就是 RunLoop 的运行阶段。
RunLoop 会不停地检查事件源和定时器,并且在有任务需要处理时,会立即处理这些任务。

3-处理事件:
RunLoop 会不断地从事件源(例如用户输入、网络请求等)中获取事件,
并将这些事件分发到合适的处理器(例如事件处理器、定时器处理器等)中进行处理。
处理完一个事件后,RunLoop 可能会再次进入休眠状态,等待下一个事件的到来。

4-等待和唤醒:
如果没有事件需要处理,RunLoop 将进入休眠状态,这个过程称为等待。
当有事件到来时,RunLoop 将被唤醒,并立即开始处理事件。
RunLoop 的等待和唤醒是动态的,会根据系统的实际情况进行调整,以提高效率和节省资源。

5-退出循环:
当应用程序即将退出时,RunLoop 会停止运行,循环结束。
这通常发生在应用程序的主线程即将退出时,例如用户关闭应用程序或者系统即将关机。

总的来说,RunLoop 是一个事件处理循环,负责处理各种事件并保持主线程的活动状态。它
的设计目的是使应用程序能够高效地响应用户的操作,并且保持界面的流畅和响应性。

2.10 用户态和内核态是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
用户态(User Mode)和内核态(Kernel Mode)是指计算机系统中操作系统与应用程序之间的两种运行模式,
它们之间的切换又称为态转换(Mode Switching)。
这两种模式具有不同的权限级别和访问系统资源的能力。

1-用户态(User Mode):

1.1-用户态是指应用程序运行的模式,应用程序在用户态下执行时,只能访问受限的资源和执行受限的操作,
例如执行应用程序自身的代码、读写应用程序自身的内存等。
1.2-在用户态下运行的程序不能直接访问系统的硬件资源,也不能执行特权指令(例如修改内核数据结构或控制硬件设备)。
1.3-大多数应用程序和用户进程都运行在用户态下,这样可以提高系统的安全性和稳定性,因为用户程序无法直接对系统资源造成破坏。

2-内核态(Kernel Mode):

2.1-内核态是指操作系统内核运行的模式,内核态下的代码拥有最高的权限和访问系统资源的能力,
可以执行任何操作系统提供的功能和服务,包括管理系统硬件资源、调度进程、执行文件系统操作等。
2.2-内核态下的代码可以访问整个系统的内存空间和硬件资源,并且可以执行特权指令,
例如修改内核数据结构、访问硬件设备等。
2.3-操作系统内核通常以内核态的方式运行,以便能够有效地管理系统资源,并且保护系统免受不良程序的影响。

在实际运行中,应用程序和操作系统之间的切换是通过系统调用(System Call)来实现的。
当应用程序需要访问系统资源或执行特权操作时,会通过系统调用进入内核态,
操作系统内核在内核态下执行相应的操作,并将结果返回给应用程序,然后应用程序再次回到用户态继续执行。

2.11 线程保活怎么做?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
线程保活是指确保线程在需要时始终处于活动状态,不被系统或其他因素中断或销毁。
线程保活通常在以下情况下需要:

1-长时间任务:当线程执行长时间任务时,需要确保线程不被系统或其他因素中断,以完成任务。

2-异步任务:在使用异步任务或后台任务时,需要保证线程一直处于活动状态,以保证任务能够顺利执行。

下面是一些线程保活的常用方法:

1-RunLoop:在线程中使用RunLoop是一种常见的线程保活方法。
通过在RunLoop中添加定时器或者持续监听事件等方式,可以使线程保持活跃状态。
需要注意的是,使用RunLoop时需要避免阻塞主线程。

2-定时器:在线程中使用定时器定期执行任务,可以保证线程不会被系统销毁。
可以使用GCD的定时器dispatch_source_create或者NSTimer来实现定时任务。

3-Dispatch I/O:使用GCD的Dispatch I/O机制,可以在后台线程执行I/O操作,保证线程一直处于活跃状态。

4-保持强引用:确保线程对象在使用过程中保持强引用,防止被意外释放。
可以将线程对象作为属性存储在其他对象中,或者使用全局变量等方式保持引用。

5-自定义循环:在线程中使用自定义的循环来执行任务,可以控制任务的执行时机和周期,从而保持线程活跃。

6-NSOperationQueue:使用NSOperationQueue管理线程,
可以方便地控制线程的生命周期和任务执行顺序,从而保证线程一直处于活跃状态。

无论选择哪种方法,都需要注意避免资源泄漏和线程阻塞,确保线程能够正常执行任务并及时释放资源。

三 参考

  • 简书—OC中Runloop原理与使用