今天整理一下运行循环来帮朋友们了解一下什么是运行循环,也叫消息循环,建议大家称之为运行循环(Runloop).
===
关于多线程的运行循环(消息循环)
1. 什么是运行循环
(1)Runloop就是运行循环,每个线程内部都有一个运行循环.
(2)只有主线程的运行循环默认是开启的,子线程的运行循环.
2. 运行循环的作用
(1)保证程序不退出,iOS的应用程序启动之后,之所以不会退出,就是因为有Runloop(运行循环).运行循环是一个死循环,只有满足一定条件才会结束循环.
(2)负责处理输入事件
(3)如果没有事件发生,会让程序进入休眠状态.
3.输入事件
Runloop接收的事件来自两种不同的来源:输入源 和 定时源.
(1)输入源(Input sources)输入源传递异步事件,通常消息来自于其他线程或程序。
(2)定时源(Timer sounces)定时源则传递同步事件,发生在特定时间或者重复的时间间隔。两种源都使用程序的某一特定的处理例程来处理到达的事件。
(3)下图展示了运行循环的概念结构以及事件来源的种类:
输入源能够通过runUntilDate 方法使线程退出;定时源不能使线程退出.
4.运行循环的模式
运行循环模式是输入源和定时源的一个集合,这个集合会被监听.
每次启动运行循环,可以指定一个特殊的模式,在运行循环执行期间,只有跟特定的模式相关联的事件源才会被监听以及允许传递它们的事件。跟其它模式相关联的事件源将不会被监听。因此,可以通过运行循环来过滤掉一些不期望的事件
下图是系统定义的几种运行循环模式:
Mode | Name | Description |
---|---|---|
Default | NSDefaultRunLoopMode(Cocoa)kCFRunLoopDefaultMode (Core Foundation) | The default mode is the one used for most operations. Most of the time, you should use this mode to start your run loop and configure your input sources. |
Connection | NSConnectionReplyMode(Cocoa) | Cocoa uses this mode in conjunction with NSConnection objects to monitor replies. You should rarely need to use this mode yourself |
Modal | NSModalPanelRunLoopMode(Cocoa) | Cocoa uses this mode to identify events intended for modal panels. |
Event tracking | NSEventTrackingRunLoopMode(Cocoa) | Cocoa uses this mode to restrict incoming events during mouse-dragging loops and other sorts of user interface tracking loops. |
Common modes | NSRunLoopCommonModes(Cocoa)kCFRunLoopCommonModes (Core Foundation) | This is a configurable group of commonly used modes. Associating an input source with this mode also associates it with each of the modes in the group. For Cocoa applications, this set includes the default, modal, and event tracking modes by default. Core Foundation includes just the default mode initially. You can add custom modes to the set using theCFRunLoopAddCommonMode function. |
大致的意思是指:
(1)Default: default模式可以用于大多数操作。在大多数时间,应该使用这种模式来启动和设置输入源。
(2)Connection: Cocoa使用这种模式联合NSConnection对象来监听响应。我们很少会自己用到这种模式
(3)Modal: Cocoa使用这种模式来识别为模态面板准备的事件。
(4)Event tracking: Cocoa使用这种模式来约束鼠标拖拽或其它用户界面追踪循环的事件。
(5)Common modes: 这是一个通用的模式组,使用这种模式关联输入源,同样会关联这个模式组里面的每一种模式。对于Cocoa应用来说,这个集合包含了 default、modal以及event tracking模式。
Core Foundation初始状态下只包含 default 模式,但是可以
通过 CFRunLoopAddCommonMode函数来添加自定义模式。
5.通过定时器演示运行循环的模式
注意:定时器执行的方法中不宜执行太耗时的操作,否则会降低用户体验.
- 第一步:创建定时器
在viewDidLoad方法中:
//1,创建定时器
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:
self selector:@selector(demo) userInfo:nil repeats:YES];
//demo方法
- (void)demo {
NSLog(@"hello");
}
- 第二步:将定时器添加到当前线程的运行循环
//将定时器添加到当前线程的运行循环
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
===
注意:这里的模式对应的是输入事件(定时器timer)的模式.
- 第三步:运行程序,执行结果如下:
当程序运行起来之后,打印输出会按照定时器,每秒输出一次.但是,如果拖动界面的控件,打印输出就会停止(即不再打印输出).
思考:为什么?
- 第四步: 打印当前运行循环的模式
demo方法中:
NSLog(@"---%@", [NSRunLoop currentRunLoop].currentMode);
执行程序:
注意:[NSRunLoop currentRunLoop].currentMode 中的模式对应的是运行循环的模式。
结论:
运行循环是在一个指定的模式下运行的(默认是NSDefaultRunLoopMode),设置的输入事件也需要指定一个模式,运行循环的模式必须和输入事件的模式相匹配才会执行。
一开始,输入事件模式是NSDefaultRunLoopMode,运行循环默认也是NSDefaultRunLoopMode,所以可以正常输出结果。
拖动控件,之所以不会打印输出,就是因为运行循环的模式发生了改变,变成了 UITrackingRunLoopMode,这个时候二者模式不匹配,所以不能正常输出。
总结:
(1)运行循环是在一个指定的模式下运行的,输入事件也有对应的模式,只有当二者的模式相匹配,对应的方法才会执行。
(2)运行循环模式:
1> 运行循环启动后,默认模式为 NSRunLoopDefaultMode
2> 如果滚动scrollView,运行循环模式为 UITrackingRunLoopMode。
子线程的运行循环
主线程的运行循环默认是开启的,子线程的运行循环默认是不开启的。
//第一步: 创建子线程
viewDidLoad方法中:
// 创建子线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
[thread start];
//第二步: 往子线程的运行循环添加输入源
// 往子线程的运行循环上添加输入源
[self performSelector:@selector(demo1) onThread:thread withObject:
nil waitUntilDone:NO];
// 执行在子线程的方法
- (void)demo {
NSLog(@"I'm running");
}
// 执行在子线程的运行循环中的方法
- (void)demo1 {
- NSLog(@"I'm running on runloop");
}
第三步: 开启子线程的运行循环
- (void)demo {
NSLog(@"I'm running");
// 这些数入源能够防止运行循环退出, 即 只要有数入源, run 方法启动的运行循环就不会退出
// Those sources could therefore prevent the run loop from exiting.
[[NSRunLoop currentRunLoop] run];
NSLog(@"end");
}
然后执行程序得到以下结果:
由输出结果可知: 没有打印 end,即只要有输入源,使用run方法启动的运行循环不会退出。
第五步: 通过 runUntilDate来启动运行循环
// 2秒钟之后消息循环结束
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
这样的话,2秒钟之后,子线程的运行循环就会退出。
希望能帮到大家!