7-1.【OC】【RunLoop】CFRunLoop 与 NSRunLoop 的关系是什么?它们共享数据结构吗?性能差异在哪里?

5 阅读2分钟

在 iOS 和 macOS 开发中,RunLoop 是维持应用持续运行的核心机制。理解 NSRunLoopCFRunLoop 的关系,本质上是理解 Foundation 框架与 Core Foundation 框架之间的“桥接”设计。


1. 两者的关系:封装与被封装

NSRunLoop 是对 CFRunLoop面向对象封装

  • CFRunLoop (Core Foundation 层) :由纯 C 语言实现。它是 RunLoop 的底层核心,提供了绝大部分的功能实现。它是线程安全的。
  • NSRunLoop (Foundation 层) :由 Objective-C 实现。它提供了一套更易用的面向对象 API。但要注意,它不是线程安全的。

2. 它们共享数据结构吗?

是的,它们操作的是同一个底层对象。

当你调用 [NSRunLoop currentRunLoop] 时,底层实际上是调用了 CFRunLoopGetCurrent()

  • NSRunLoop 内部持有一个 CFRunLoopRef 的实例。
  • 你可以通过 NSRunLoopgetCFRunLoop 方法(Swift 中是 getCFRunLoop())轻松地在这两者之间转换。

底层逻辑: 每一个线程都有且仅有一个与之对应的 CFRunLoop。无论是通过 NSRunLoop 还是 CFRunLoop 访问,最终指向的都是存储在全局字典(以线程为 Key)里的那个同一个 CFRunLoop 结构。


3. 性能差异在哪里?

在大多数业务开发场景下,两者的执行性能差异几乎可以忽略不计,因为 NSRunLoop 的方法调用开销相对于 RunLoop 内部的 mach_msg 睡眠和唤醒机制来说非常小。

然而,真正的“性能”差异体现在以下几个维度:

A. 线程安全性(核心差异)

  • CFRunLoop:可以在多线程环境下安全地添加 Source、Timer 或 Observer。
  • NSRunLoop:因为其非线程安全特性,官方建议只在当前 RunLoop 所属的线程中操作它。跨线程操作 NSRunLoop 可能导致未定义的行为或数据竞态。

B. 功能覆盖度

  • CFRunLoop 更加底层且功能完整。许多底层的控制(如 CFRunLoopObserver 的精细回调、判断 RunLoop 是否已经停止等)只能通过 CFRunLoop 实现。
  • NSRunLoop 为了易用性简化了一些功能。例如,NSRunLoop 没有提供直接添加 Observer 的方法。

C. 启动开销

NSRunLoop 的 API 往往会自动处理一些逻辑(如 Mode 的切换),这在带来方便的同时,也增加了一点点方法派发的开销。


4. 选型建议

场景推荐使用原因
通用 UI 逻辑 / TimerNSRunLoopAPI 友好,符合 OC/Swift 习惯。
底层性能监控CFRunLoop需要添加 Observer 来监控 RunLoop 状态。
跨线程控制 RunLoopCFRunLoop线程安全,能更稳定地进行添加/移除操作。
精细控制运行模式 (Mode)CFRunLoop支持自定义 Mode 及更复杂的 RunLoop 运行控制。

💡 一个关键的小细节

如果你正在使用 Swift,苹果更倾向于让你使用 RunLoop 类(即 NSRunLoop 的 Swift 版本),但在处理高性能、高精度的计时器或需要监控主线程卡顿时,依然需要通过 RunLoop.current.getCFRunLoop() 降级回 CFRunLoop 来操作。