最近在准备 iOS 相关的面试题,打算从实际应用中学习、掌握基本概念
RunLoop与线程的关系:每个线程都有一个唯一的RunLoop对象,主线程的RunLoop默认是启动的,而子线程的RunLoop需要手动启动
RunLoop的模式(Mode):RunLoop在不同的模式下运行,每个模式包含一组输入源(Source)、定时器(Timer)和观察者(Observer)。常见的模式有:
NSDefaultRunLoopMode:默认模式,空闲状态。
UITrackingRunLoopMode:滚动时的模式。
NSRunLoopCommonModes:一个通用模式集合,包括前两种。RunLoop的事件源:
Source0:非基于Port的事件,需要手动触发。
Source1:基于Port的事件,通过内核和其他线程通信。
Timers:定时器事件。
Observers:观察者,监听RunLoop的状态变化。
一:保持线程常驻
class PersistentThread: Thread {
var runLoop: RunLoop?
override func main() {
autoreleasepool {
// 获取当前线程的RunLoop
self.runLoop = RunLoop.current
// 添加Port防止RunLoop立即退出
let port = Port()
self.runLoop?.add(port, forMode: .default)
print("子线程RunLoop启动")
// 启动RunLoop,线程会保持活跃
self.runLoop?.run()
print("子线程RunLoop退出")
}
}
}
// 使用示例
class ViewController: UIViewController {
var persistentThread: PersistentThread?
func example1() {
persistentThread = PersistentThread()
persistentThread?.start()
// 给线程一点时间启动RunLoop
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// 在常驻线程执行任务
self.perform(#selector(self.doBackgroundTask),
on: self.persistentThread!,
with: nil,
waitUntilDone: false)
}
}
@objc func doBackgroundTask() {
print("在常驻线程执行耗时任务: \(Thread.current)")
}
}
二:处理Timer的滚动冲突
class TimerViewController: UIViewController {
var timer1: Timer?
var timer2: Timer?
var count = 0
func setupTimer() {
// 方式1:默认模式 - 滚动时暂停
timer1 = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
self?.timerFired()
}
// 默认已经添加到DefaultMode
// 方式2:CommonModes - 滚动时也触发
timer2 = Timer(timeInterval: 1.0, repeats: true) { [weak self] _ in
self?.timerFired()
}
RunLoop.current.add(timer2!, forMode: .common)
}
func timerFired() {
count += 1
print("定时器触发: \(count)")
}
// Swift中的简化版本(iOS 10+推荐)
func setupModernTimer() {
// 使用Timer的block API(iOS 10+)
_ = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
print("现代Timer API")
}
}
}
检测卡顿
import Foundation
class LagMonitor {
private var semaphore: DispatchSemaphore?
private var activity: CFRunLoopActivity = .entry
private var isMonitoring = false
func startMonitoring() {
guard !isMonitoring else { return }
isMonitoring = true
semaphore = DispatchSemaphore(value: 0)
// 观察主线程RunLoop
var context = CFRunLoopObserverContext(
version: 0,
info: Unmanaged.passUnretained(self).toOpaque(),
retain: nil,
release: nil,
copyDescription: nil
)
guard let observer = CFRunLoopObserverCreate(
kCFAllocatorDefault,
CFRunLoopActivity.allActivities.rawValue,
true,
0,
runLoopObserverCallBack,
&context
) else { return }
CFRunLoopAddObserver(RunLoop.main.getCFRunLoop(), observer, .commonModes)
// 在子线程监控时长
DispatchQueue.global(qos: .background).async { [weak self] in
guard let self = self else { return }
while self.isMonitoring {
// 等待信号量,如果超时50ms(1/20秒)说明可能卡顿
let waitResult = self.semaphore?.wait(timeout: .now() + .milliseconds(50))
if waitResult == .timedOut { // 超时
if self.activity == .beforeSources ||
self.activity == .afterWaiting {
// 注意:这里要回到主线程处理UI相关操作
DispatchQueue.main.async {
print("⚠️ 检测到卡顿!活动状态: \(self.activity.rawValue)")
// 可以在这里记录堆栈信息或上报
}
}
}
}
}
}
func stopMonitoring() {
isMonitoring = false
}
private func runLoopObserverCallBack(activity: CFRunLoopActivity) {
self.activity = activity
self.semaphore?.signal()
}
}
// 全局函数作为CFRunLoopObserver的回调
private func runLoopObserverCallBack(
observer: CFRunLoopObserver?,
activity: CFRunLoopActivity,
info: UnsafeMutableRawPointer?
) {
guard let info = info else { return }
let monitor = Unmanaged.fromOpaque(info).takeUnretainedValue()
monitor.runLoopObserverCallBack(activity: activity)
}