iOS开发基础122-RunLoop
1.iOS开发基础77-一像素线的几种实现方式2.iOS开发基础78-iOS 国际化3.iOS开发基础82-关于iOS目录4.iOS开发基础81-Runtime实战5.iOS开发基础80-关于Xcode86.iOS开发基础79-强制退出程序7.iOS开发基础90-密码学8.iOS开发基础89-Runloop9.iOS开发基础88-涂鸦效果10.iOS开发基础87-抽屉效果11.iOS开发基础86-FMDB12.iOS开发基础85-线程dispatch apply13.iOS开发基础84-HTTP请求方法详解与增删改查的应用14.iOS开发基础83-线程组15.iOS开发基础106-Instruments16.iOS开发基础105-Xcode收集Crashs的各种方法17.iOS开发基础104-正向代理和反向代理18.iOS开发基础103-APP之间跳转19.iOS开发基础102-后台保活方案20.iOS开发基础101-指纹和面部识别21.iOS开发基础100-MDM证书申请流程22.iOS开发基础99-iOS 内购的防范与优化23.iOS开发基础98-跳转淘宝案例24.iOS开发基础97-应用内购(In-App Purchase)的安全性解析与收据处理流程25.iOS开发基础96-UI类继承关系图26.iOS开发基础95-程序内评价27.iOS开发基础94-xcode1028.iOS开发基础93-GCD死锁29.iOS开发基础92-线程保活30.iOS开发基础91-线程同步技术与资源共享详解31.iOS开发基础138-视频编码32.iOS开发基础137-音视频编解码简介33.iOS开发基础136-防暴力点击34.iOS开发基础135-Core Data35.iOS开发基础134-异步并行上传问题36.iOS开发基础133-崩溃预防37.iOS开发基础132-POSIX线程库38.iOS开发基础131-isa指针39.iOS开发基础130-视频录制上传40.iOS开发基础129-音频录制上传41.iOS开发基础128-应用本地化42.iOS开发基础127-深入探讨KVO43.iOS开发基础126-深入探索设计模式44.iOS开发基础125-深入探索SDWebImage45.iOS开发基础124-RunLoop实现卡顿检测46.iOS开发基础123-自动释放池原理
47.iOS开发基础122-RunLoop
48.iOS开发基础121-APP启动优化49.iOS开发基础120-通知与线程50.iOS开发基础119-组件化51.iOS开发基础118-Runtime52.iOS开发基础117-Hybrid53.iOS开发基础116-性能监控54.iOS开发基础115-Socket55.iOS开发基础114-YYCache56.iOS开发基础113-Unity3D57.iOS开发基础112-GCD常见场景58.iOS开发基础111-RAC59.iOS开发基础110-Core Graphics应用场景60.iOS开发基础109-网络安全61.iOS开发基础108-常见的编程范式62.iOS开发基础107-iOS直播63.iOS开发基础148-ABM vs MDM64.iOS开发基础147-ABM集中管理Apple设备65.iOS开发基础146-深入解析WKWebView66.iOS开发基础145-Apple Search Ads67.iOS开发基础144-逐字打印效果68.iOS开发基础143-性能优化69.iOS开发基础142-广告归因70.iOS开发基础141-音频解码71.iOS开发基础140-音频编码72.iOS开发基础139-视频解码73.iOS开发基础149-由UUIDString引发的思考深入探讨 RunLoop
的底层实现需要了解 Core Foundation 框架中的 CFRunLoop
以及与 RunLoop
工作机制紧密相关的操作系统底层 API。这些底层实现主要涉及到事件源、定时器和线程的调度机制。本文将深入剖析 RunLoop
的底层结构及其运行流程。
一、RunLoop 底层数据结构
涉及 RunLoop
的核心数据结构主要包括:
- CFRunLoopRef :表示一个
RunLoop
对象。 - CFRunLoopModeRef :表示
RunLoop
的运行模式。每个RunLoop
可以有多个模式,但同一时刻只能使用一个模式。 - CFRunLoopSourceRef :表示输入源,用于处理异步事件。
- CFRunLoopTimerRef :表示定时器事件。
- CFRunLoopObserverRef :观察者,用于监听
RunLoop
的状态变化。
每个 RunLoop
都维护一个输入源列表、定时器列表和观察者列表,并按模式分组管理。
二、RunLoop 的核心组件和工作机制
1. CFRunLoopMode
RunLoop
有五种预定义模式:
- kCFRunLoopDefaultMode :默认模式,通常 UI 操作、定时器等在这个模式下运行。
- UITrackingRunLoopMode :用于检测用户 UI 交互的模式,如滚动等。
- kCFRunLoopCommonModes :一个伪类别的模式,被标记为公共模式的事件源会应用于所有的公共模式。
- GSEventReceiveRunLoopMode :基础设施的一部分,用于接收系统事件。
- kCFRunLoopDefaultMode :具体用于 CA。
每个 CFRunLoopMode
包含以下内容:
- Sources0 :不自动触发的输入源。
- Sources1 :基于端口的输入源。
- Timers :定时器。
- Observers :状态观察者。
2. CFRunLoopSource
CFRunLoopSource
分为两类:
- Source0 :手动触发,由应用负责管理。
- Source1 :基于内核机制(如端口、Socket等)的输入源,自动触发。
3. CFRunLoopTimer
定时器事件,基于时间的触发机制。CFRunLoopTimer
会在设置的时间间隔后被触发,并且可以配置为重复或单次触发。
4. CFRunLoopObserver
用于监控 RunLoop
的状态变化,包含以下几种状态:
- kCFRunLoopEntry :进入
RunLoop
。 - kCFRunLoopBeforeTimers :准备处理定时器。
- kCFRunLoopBeforeSources :准备处理输入源。
- kCFRunLoopBeforeWaiting :即将进入休眠。
- kCFRunLoopAfterWaiting :刚从休眠中唤醒。
- kCFRunLoopExit :退出
RunLoop
。
三、RunLoop 实现原理
1. RunLoop 运行的主要流程
- 创建
CFRunLoopMode
:初始化并添加各种事件源(Source、Timer、Observer)到对应的 Mode 中。 - 运行
CFRunLoopRun
方法 :按如下步骤进行:
void CFRunLoopRun(void) {
CFRunLoopRef rl = CFRunLoopGetCurrent();
CFRunLoopRunSpecific(rl, kCFRunLoopDefaultMode, 1.0e10, false); // 默认模式,无超时
}
void CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, mode);
if(!currentMode) return;
while (1) {
// 通知观察者即将进入 RunLoop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 处理已到时间的定时器
__CFRunLoopDoTimers(rl, currentMode, mach_absolute_time());
// 处理输入源(Source0 和 Source1)
__CFRunLoopDoSources(rl, currentMode, mach_absolute_time());
// 通知观察者即将进入休眠
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopBeforeWaiting);
// 进入休眠等待事件
__CFRunLoopWait(rl, currentMode);
// 从休眠中唤醒
__CFRunLoopHandleWakeUpSources(rl, currentMode);
// 通知观察者已从休眠中唤醒
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopAfterWaiting);
// 处理定时器
__CFRunLoopDoTimers(rl, currentMode, mach_absolute_time());
// 处理输入源
if(__CFRunLoopDoSources(rl, currentMode, mach_absolute_time())) break;
}
__CFRunLoopModeUnlock(currentMode);
}
2. 休眠和唤醒机制
RunLoop
的底层依靠系统 API 实现休眠和唤醒:
- 休眠:使用
mach_msg
和select
等系统调用,将线程睡眠,等待事件触发。 - 唤醒:通过
mach_port
和CFRunLoopSource1
来唤醒线程。
void __CFRunLoopWait(CFRunLoopRef rl, CFRunLoopModeRef rlm) {
mach_msg_timeout_t timeout = TIMEOUT_INFINITY; // 无限期等待
int32_t result = mach_msg(..., timeout, ...);
if (result == MACH_MSG_SUCCESS) {
return result;
}
}
四、应用场景的实现原理及代码
1. 保持后台线程存活
- (void)startBackgroundThread {
[NSThread detachNewThreadSelector:@selector(threadEntryPoint) toTarget:self withObject:nil];
}
- (void)threadEntryPoint {
@autoreleasepool {
[[NSThread currentThread] setName:@"BackgroundThread"];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
2. 管理定时器任务
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
3. 处理 UI 事件
UI 事件处理的核心在于主线程的 RunLoop
,包含屏幕渲染、事件分发等。 Cocoa Touch 框架将这些操作都自动集成在主线程的 RunLoop
中,确保 UI 事件的及时响应。
// 主线程默认 RunLoop
[[NSRunLoop mainRunLoop] run];
4. 异步网络请求
网络请求使用 NSURLSession
的回调机制,可以在 RunLoop
中进行处理。
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://example.com"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (data) {
// 更新 UI
}
});
}];
[task resume];
5. 自动释放池的管理
每次 RunLoop
执行一次循环时,系统会自动构建和销毁一个自动释放池。
- (void)runLoopWithAutoreleasePool {
while (self.keepRunning) {
@autoreleasepool {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
}
总结
通过深入分析 RunLoop
的底层实现,我们可以了解该机制如何高效地调度任务和处理事件,从而保持应用的响应性。掌握 RunLoop
的原理有助于开发者在日常编码中更高效地进行系统调优和解决复杂问题。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!
2015-07-17 iOS开发基础10-UIButton内边距和图片拉伸模式
2015-07-17 iOS开发基础9-提示框(UIAlertController)