本文深入探讨 iOS 中 RunLoop 的实现原理、工作机制以及实际应用。通过源码分析和实际案例,帮助读者全面理解 RunLoop 在 iOS 系统中的重要作用。
RunLoop 是 iOS 系统中用于处理事件和消息的循环机制。它负责管理线程的事件处理、消息传递和任务调度,是 iOS 应用能够持续运行并响应用户交互的基础。
主要功能:
CFRunLoopRef
├── CFRunLoopModeRef (多个)
│ ├── CFRunLoopSourceRef (Source0)
│ ├── CFRunLoopSourceRef (Source1)
│ ├── CFRunLoopTimerRef
│ └── CFRunLoopObserverRef
├── CFRunLoopCommonModes
└── CFRunLoopCommonModeItems
RunLoop 的核心组件形成了一个完整的层级结构,从顶层到底层依次为:
CFRunLoopRef:RunLoop 的核心,负责管理整个 RunLoop 的生命周期、状态和所有子组件。
CFRunLoopModeRef:RunLoop 的运行模式,用于隔离不同场景下的事件处理,每个 Mode 包含独立的事件源集合。
CFRunLoopSourceRef:RunLoop 的事件源,分为处理应用内部事件的 Source0 和处理系统事件的 Source1。
CFRunLoopTimerRef:RunLoop 的定时器,用于在特定时间点触发事件,支持重复触发和单次触发。
CFRunLoopObserverRef:RunLoop 的观察者,用于监听 RunLoop 的状态变化,支持监控整个生命周期。
CFRunLoopRef 是 RunLoop 的核心对象,它包含了 RunLoop 的所有状态和配置信息。在 Core Foundation 中,RunLoop 是一个 C 语言结构体,通过 CFRunLoopRef 进行引用。
底层结构:
struct __CFRunLoop {
CFRuntimeBase _base; // 基础运行时信息
pthread_mutex_t _lock; // 互斥锁,保证线程安全
CFStringRef _currentMode; // 当前运行模式
CFMutableSetRef _modes; // 所有模式集合
CFMutableSetRef _commonModes; // 通用模式集合
CFMutableSetRef _commonModeItems; // 通用模式项集合
CFRunLoopModeRef _currentMode; // 当前模式引用
CFMutableSetRef _sources0; // Source0 集合,处理应用内部事件
CFMutableSetRef _sources1; // Source1 集合,处理系统事件
CFMutableArrayRef _observers; // 观察者数组,监听 RunLoop 状态变化
CFMutableArrayRef _timers; // 定时器数组,管理定时任务
};
CFRunLoopMode 定义了 RunLoop 的运行模式,每个 RunLoop 可以包含多个 Mode,但同一时间只能运行在一个 Mode 下。Mode 的主要作用是隔离不同场景下的事件处理。
底层结构:
struct __CFRunLoopMode {
CFRuntimeBase _base;
CFStringRef _name; // 模式名称
CFMutableSetRef _sources0; // Source0 集合
CFMutableSetRef _sources1; // Source1 集合
CFMutableArrayRef _observers; // 观察者数组
CFMutableArrayRef _timers; // 定时器数组
};
常见模式:
CFRunLoopSource 是 RunLoop 的事件源,分为两种类型:
Source0:处理应用内部事件
Source1:处理系统事件
底层结构:
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits; // 标志位,用于标识 Source 的类型和状态
pthread_mutex_t _lock; // 互斥锁,保证线程安全
CFIndex _order; // 优先级顺序,决定处理顺序
CFMutableBagRef _runLoops; // 关联的 RunLoop 集合
union {
CFRunLoopSourceContext version0; // Source0 上下文
CFRunLoopSourceContext1 version1; // Source1 上下文
} _context;
};
CFRunLoopTimer 是基于时间的触发器,用于在特定时间点触发事件。NSTimer 就是基于 RunLoop 的 Timer 实现的。
底层结构:
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits; // 标志位,用于标识 Timer 的状态
pthread_mutex_t _lock; // 互斥锁,保证线程安全
CFRunLoopRef _runLoop; // 关联的 RunLoop
CFMutableSetRef _rlModes; // 运行模式集合
CFAbsoluteTime _nextFireDate; // 下次触发时间
CFTimeInterval _interval; // 时间间隔
CFTimeInterval _tolerance; // 时间容差,允许的误差范围
uint64_t _fireTSR; // 触发时间戳
CFIndex _order; // 优先级顺序
CFRunLoopTimerCallBack _callout; // 回调函数
CFRunLoopTimerContext _context; // 上下文信息
};
CFRunLoopObserver 用于观察 RunLoop 的状态变化,可以监听 RunLoop 的整个生命周期。
底层结构:
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock; // 互斥锁,保证线程安全
CFRunLoopRef _runLoop; // 关联的 RunLoop
CFIndex _rlCount; // RunLoop 计数
CFOptionFlags _activities; // 观察的活动
CFIndex _order; // 优先级顺序
CFRunLoopObserverCallBack _callout; // 回调函数
CFRunLoopObserverContext _context; // 上下文信息
};
可监听的事件:
RunLoop 的运行流程是一个循环过程,主要包含以下步骤:
通过 RunLoop Observer 可以监控主线程的卡顿情况,这是一个非常实用的性能监控工具。以下是一个完整的卡顿监控实现:
class RunLoopMonitor {
private var observer: CFRunLoopObserver?
private var lastActivity: CFRunLoopActivity = .entry
private var lastTime: TimeInterval = 0
private let semaphore = DispatchSemaphore(value: 0)
func startMonitor() {
// 创建观察者
var context = CFRunLoopObserverContext(
version: 0,
info: Unmanaged.passUnretained(self).toOpaque(),
retain: nil,
release: nil,
copyDescription: nil
)
observer = CFRunLoopObserverCreate(
kCFAllocatorDefault,
CFRunLoopActivity.allActivities.rawValue,
true,
0,
runLoopObserverCallBack,
&context
)
// 将观察者添加到主线程的 RunLoop
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, .commonModes)
// 在子线程中监控卡顿
DispatchQueue.global().async { [weak self] in
guard let self = self else { return }
while true {
// 等待 50ms
let semaphoreWait = self.semaphore.wait(timeout: .now() + 0.05)
if semaphoreWait == .timedOut { // 超时
if self.lastActivity == .beforeSources ||
self.lastActivity == .afterWaiting {
// 检测到卡顿,记录堆栈信息
self.logStackInfo()
}
}
}
}
}
private let runLoopObserverCallBack: CFRunLoopObserverCallBack = { (observer, activity, info) in
guard let info = info else { return }
let monitor = Unmanaged<RunLoopMonitor>.fromOpaque(info).takeUnretainedValue()
monitor.lastActivity = activity
monitor.lastTime = Date().timeIntervalSince1970
monitor.semaphore.signal()
}
private func logStackInfo() {
// 获取当前线程的堆栈信息
let callStackSymbols = Thread.callStackSymbols
print("卡顿堆栈信息:\(callStackSymbols)")
}
}
这段代码实现了一个完整的卡顿监控系统:
class ScrollViewOptimizer: NSObject, UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 在滚动时暂停某些操作
perform(#selector(heavyOperation),
with: nil,
afterDelay: 0,
inModes: [.default])
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
// 在滚动结束后恢复操作
perform(#selector(heavyOperation),
with: nil,
afterDelay: 0,
inModes: [.common])
}
@objc private func heavyOperation() {
// 耗时操作
}
}
这段代码展示了如何利用 RunLoop Mode 来优化滚动性能:
class TimerOptimizer {
private var timer: Timer?
private let tolerance: TimeInterval = 0.1 // 100ms 的容差
func setupOptimizedTimer() {
// 创建定时器
timer = Timer(timeInterval: 1.0,
target: self,
selector: #selector(timerAction),
userInfo: nil,
repeats: true)
// 设置时间容差,提高性能
timer?.tolerance = tolerance
// 添加到 RunLoop
RunLoop.current.add(timer!, forMode: .common)
}
@objc private func timerAction() {
// 执行定时任务
print("Timer fired at: \(Date())")
}
}
这段代码展示了如何优化定时器的使用:
class BackgroundWorker {
private var workerThread: Thread?
private var shouldKeepRunning = false
func start() {
shouldKeepRunning = true
workerThread = Thread(target: self, selector: #selector(workerThreadEntry), object: nil)
workerThread?.start()
}
@objc private func workerThreadEntry() {
autoreleasepool {
// 获取当前线程的 RunLoop
let runLoop = RunLoop.current
// 添加 Port 防止 RunLoop 退出
let port = Port()
runLoop.add(port, forMode: .default)
// 添加观察者监控 RunLoop 状态
let observer = CFRunLoopObserverCreateWithHandler(
kCFAllocatorDefault,
CFRunLoopActivity.allActivities.rawValue,
true,
0
) { (observer, activity) in
switch activity {
case .entry:
print("RunLoop 进入")
case .exit:
print("RunLoop 退出")
default:
break
}
}
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, .defaultMode)
// 运行 RunLoop
while shouldKeepRunning {
runLoop.run(mode: .default, before: .distantFuture)
}
}
}
func stop() {
shouldKeepRunning = false
perform(#selector(stopThread),
on: workerThread!,
with: nil,
waitUntilDone: false)
}
@objc private func stopThread() {
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
这段代码实现了一个完整的常驻线程:
class EventResponder {
private var eventQueue: [Any] = []
private var isProcessing = false
func handleEvent(_ event: Any) {
eventQueue.append(event)
if !isProcessing {
isProcessing = true
perform(#selector(processNextEvent),
with: nil,
afterDelay: 0,
inModes: [.default])
}
}
@objc private func processNextEvent() {
guard !eventQueue.isEmpty else {
isProcessing = false
return
}
let event = eventQueue.removeFirst()
processEvent(event)
// 继续处理下一个事件
perform(#selector(processNextEvent),
with: nil,
afterDelay: 0,
inModes: [.default])
}
private func processEvent(_ event: Any) {
// 实际的事件处理逻辑
print("Processing event: \(event)")
}
}
这段代码展示了如何优化事件响应:
NSTimer 不触发
子线程任务不执行
主线程卡顿
RunLoop 是 iOS 系统中处理事件和消息的核心机制,它通过循环处理来保持程序持续运行并响应各种事件。每个线程都有且只有一个对应的 RunLoop,主线程的 RunLoop 默认开启,而子线程需要手动开启。RunLoop 的核心组件包括 CFRunLoopRef、CFRunLoopMode、CFRunLoopSource、CFRunLoopTimer 和 CFRunLoopObserver,它们共同构成了一个完整的事件处理系统。RunLoop 的工作流程包括事件处理、休眠和唤醒等步骤,通过合理使用 RunLoop Mode 和优化定时器,可以有效提升应用性能。在实际应用中,RunLoop 常用于常驻线程、事件响应优化和性能监控等场景。掌握 RunLoop 的原理和应用,对于开发高性能的 iOS 应用至关重要。
如果觉得本文对你有帮助,欢迎点赞、收藏、关注我,后续会持续分享更多 iOS 底层原理与实战经验