iOS开发基础39-RunLoop
在iOS开发中,RunLoop
是一个极其重要的概念,它不仅负责保持应用程序的持续运行,还能处理各种事件,提高应用的性能和响应速度。。
一、什么是RunLoop
1. 定义
从字面意义上讲,RunLoop
即运行循环。它的基本作用包括:
- 保持程序的持续运行: 确保应用程序在用户关闭之前一直处于活跃状态。
- 处理各种事件: 如触摸事件、定时器事件、Selector事件等。
- 节省CPU资源: 合理分配CPU资源,在需要处理任务时处理任务,空闲时休眠。
2. 没有RunLoop的情况
int main(int argc, char * argv[]) {
NSLog(@"execute main function");
return 0;
}
在没有RunLoop
的情况下,第3行代码执行完后,程序就会立即退出。
3. 有了RunLoop
当有RunLoop
存在时,由于main
函数内部启动了一个RunLoop
,程序并不会马上退出,而是保持持续运行状态。
4. main函数中的RunLoop
在main
函数中,UIApplicationMain
函数负责启动RunLoop
:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
UIApplicationMain
函数启动RunLoop
,使其保持程序的持续运行。这个默认的RunLoop
是与主线程相关联的。
验证:
可以看到----------不会打印,一直在执行UIApplicationMain
5. 模拟RunLoop内部实现
其实RunLoop
内部就是一个do-while
循环,不断地处理各种任务,例如Source、Timer、Observer。
void message(int num) {
printf("执行第%i个任务", num);
}
int main(int argc, const char * argv[]) {
do {
printf("有事吗? 没事我睡了");
int number;
scanf("%i", &number);
message(number);
} while (1);
return 0;
}
二、RunLoop对象
1. RunLoop的API
在iOS中,有两套API可访问和使用RunLoop
:
- Foundation API:
NSRunLoop
- Core Foundation API:
CFRunLoopRef
NSRunLoop
是基于CFRunLoopRef
的OC包装,研究CFRunLoopRef
可以更深入地了解RunLoop
的内部结构。
2. RunLoop资料
苹果官方文档:Run Loop Management
CFRunLoopRef
是开源的,可以参考:CF Source Code
3. RunLoop与线程
每条线程都有一个唯一的与之对应的RunLoop
对象。
- 主线程的
RunLoop
已经自动创建。 - 子线程的
RunLoop
需要主动创建。
RunLoop
在第一次获取时创建,在线程结束是销毁。
4. 获得RunLoop对象的方法
-
Foundation (OC语言)
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象 [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
-
Core Foundation (C语言)
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象 CFRunLoopGetMain(); // 获得主线程的RunLoop对象
三、RunLoop相关类
在Core Foundation中,涉及RunLoop
的主要有5个类:
1. CFRunLoopRef
CFRunLoopRef
是RunLoop
的核心类,用于管理运行循环。
2. CFRunLoopModeRef
CFRunLoopModeRef
代表RunLoop
的运行模式。一个RunLoop
包含多个Mode,每个Mode包含若干个Source、Timer和Observer。每次启动RunLoop
时,必须指定一个Mode,这个Mode称为CurrentMode。切换Mode需要退出Loop,再重新指定。
系统注册了以下几个默认的Mode:
kCFRunLoopDefaultMode
: 应用程序的默认Mode。UITrackingRunLoopMode
: 用户界面追踪Mode。UIInitializationRunLoopMode
: 应用程序初始化时的Mode。GSEventReceiveRunLoopMode
: 系统事件接收Mode。kCFRunLoopCommonModes
: 占位用的Mode。
3. CFRunLoopSourceRef
CFRunLoopSourceRef
代表事件源(输入源),可以分为:
- Source0:非基于Port的事件源。
- Source1:基于Port的事件源。
4. CFRunLoopTimerRef
CFRunLoopTimerRef
代表基于时间的触发器,与NSTimer
类似,用于定时执行任务。
5. CFRunLoopObserverRef
CFRunLoopObserverRef
是观察者,可以监听RunLoop
的状态变化,如进入Loop、即将处理Timers、即将处理Sources、即将进入休眠、已从休眠唤醒、即将退出Loop等。
// 1.创建Observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(
kCFAllocatorDefault,
kCFRunLoopAllActivities,
YES,
0,
^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即将进入loop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即将处理timers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即将处理sources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即将进入休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"刚从休眠中唤醒");
break;
case kCFRunLoopExit:
NSLog(@"即将退出loop");
break;
default:
break;
}
}
);
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);
CFRelease(observer);
四、RunLoop处理逻辑
RunLoop
的处理逻辑可以概括为以下步骤:
- 通知观察者即将进入
RunLoop
。 - 通知观察者即将处理
Timer
。 - 通知观察者即将处理
Source
。 - 如果有定时任务,处理
Timer
。 - 如果有事件源,处理
Source
。 - 通知观察者即将进入休眠。
- 进入休眠,等待事件。
- 被事件唤醒,跳回步骤2。
- 通知观察者即将退出
RunLoop
。
五、RunLoop应用
1. NSTimer
的模式限制
NSTimer
必须添加到RunLoop
中才能执行,而且只能在指定的Mode下运行。
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// 或者通过scheduled方法创建,自动添加到默认模式
NSTimer *scheduledTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];
2. GCD定时器
GCD定时器可以在指定的队列中执行,极其高效和灵活。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), 2 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"定时器触发");
});
dispatch_resume(timer);
3. UIImageView
显示图片
UIImageView
在指定的Mode下才能设置图片,建议在主线程进行。
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:[UIImage imageNamed:@"example"] waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];
4. 常驻线程
常驻线程需要一个RunLoop
来保持活跃状态。
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}];
[thread start];
5. 自动释放池
RunLoop
在某些活动时会自动创建和释放自动释放池。常见的活动包括进入RunLoop
、即将进入休眠、唤醒、退出RunLoop
等。
结论
RunLoop
是iOS系统中至关重要的机制,通过合理配置RunLoop
,可以保持应用程序的持续运行,处理各种事件,提高应用性能。掌握RunLoop
的工作原理和应用场景,能够帮助开发者设计出更加健壮和高效的应用程序。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!