何时使用 Run Loop?
仅当在为你的程序创建辅助线程的时候,你才需要显式运行一个 run loop。Run loop 是程序主线程基础设施的关键部分。所以,Cocoa 和 Carbon 程序提供了代码运 行主程序的循环并自动启动 run loop。IOS 程序中 UIApplication 的 run 方法(或 Mac OS X 中的 NSApplication)作为程序启动步骤的一部分,它在程序正常启动的时 候就会启动程序的主循环。类似的,RunApplicationEventLoop 函数为 Carbon 程序 启动主循环。如果你使用 xcode 提供的模板创建你的程序,那你永远不需要自己去显 式的调用这些例程。
对于辅助线程,你需要判断一个 run loop 是否是必须的。如果是必须的,那么 你要自己配置并启动它。你不需要在任何情况下都去启动一个线程的 run loop。比 如,你使用线程来处理一个预先定义的长时间运行的任务时,你应该避免启动 run loop。Run loop 在你要和线程有更多的交互时才需要,比如以下情况:
使用端口或自定义输入源来和其他线程通信 使用线程的定时器 Cocoa 中使用任何 performSelector...的方法 使线程周期性工作
如果你决定在程序中使用 run loop,那么它的配置和启动都很简单。和所有线程 编程一样,你需要计划好在辅助线程退出线程的情形。让线程自然退出往往比强制关 闭它更好。关于更多介绍如何配置和退出一个 run loop,参阅”使用 Run Loop 对象” 的介绍。
作用
- 使程序一直运行并接收用户的输入
- 决定程序在何时处理哪些事件
- 调用解耦(Message Queue)
- 节省CPU时间(当程序启动后,什么都没有执行的话,就不用让CPU来消耗资源来执行,直接进入睡眠状态)
模式
RunLoop
在同一段时间只能且必须在一种特定的模式下运行- 如果要更换 Mode,必须先停止当前的 Loop,然后再重新启动 Loop
-
Mode 是保证滚动流畅的关键
-
NSDefaultRunLoopMode
:默认状态、空闲状态 UITrackingRunLoopMode
:滚动模式UIInitializationRunLoopMode
:私有的,App启动时NSRunLoopCommonModes
:默认包含1,2两种模式
模拟 RunLoop 实现
void runEvent(int num){
if (num == 1){
NSLog(@"按钮点击事件"); } if (num == 2) { NSLog(@"scrollView拖动事件"); } } int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... //NSLog(@"Hello, World!"); while (YES) { NSLog(@"请输入选择项,0表示退出"); int num =0; scanf("%d",&num); if (num==0) { break; }else{ runEvent(num); } } } return 0; }
运行循环与时钟
@interface ViewController ()
@property(nonatomic,strong)NSTimer *timer; @property(nonatomic,assign)int count; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES]; //需要将其加入到运行循环里面才能执行 [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; } - (void)demo{ self.count++; NSLog(@"hello===%d",self.count); }
- 将时钟添加到其他线程工作
@interface ViewController ()
@property(nonatomic,strong)NSTimer *timer; @property(nonatomic,assign)int count; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //1.创建一个子线程 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadRun) object:nil]; [thread start]; } /** 1.在子线程中,如下事件是不会开启的 1.定时源事件 2.输入源事件 3.网络下载 */ - (void)threadRun{ NSLog(@"%s",__func__); self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES]; //需要将其加入到运行循环里面才能执行 [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; //1.需要我们自己手工开启我们子线程的NSRunLoop CFRunLoopRun(); } - (void)demo{ self.count++; if (self.count==5) { NSLog(@"---停止子线程运行循环---"); //停止运行循环,这样子线程才能销毁 CFRunLoopStop(CFRunLoopGetCurrent()); } NSLog(@"hello===%d",self.count); }
注意:主线程的运行循环是默认启动的,但是子线程的运行循环是默认不工作的,子线程的运行循环需要我们自己手工开启和关闭,只有自己手工关闭,这样才能够保证线程执行完毕后,子线程自动被销毁