Runloop学习
| 目录 |
|: ------------- |
| 1 什么是Runloop? |
| 2 进一步了解Runloop|
| 3 Runloop示例一 |
| 4 Runloop示例二 |
1 什么是Runloop?
一个Runloop就是一个处理事件的循环,通过Runloop可以让线程在没有任务时处于睡眠状态,当有任务触发时,也可以立即处理。这里,从一个简单的例子出发,看看Runloop的使用。
//按钮1,以睡眠的方式保证线程持续运行
- (IBAction)buttonNormalThreadTestPressed:(UIButton *)sender {
NSLog(@"EnterbuttonNormalThreadTestPressed");
threadProcess1Finished =NO;
[NSThread detachNewThreadSelector:@selector(threadProce)
toTarget:self
withObject:nil];
while (!threadProcessFinished) {
[NSThread sleepForTimeInterval: 0.5];
}
NSLog(@"ExitbuttonNormalThreadTestPressed");
}
//按钮2,以Runloop的方式保证线程持续运行
- (IBAction)buttonRunloopPressed:(id)sender {
NSLog(@"Enter buttonRunloopPressed");
threadProcess2Finished =NO;
[NSThread detachNewThreadSelector:@selector(threadProce)
toTarget:self
withObject:nil];
while (!threadProcessFinished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
NSLog(@"Exit buttonRunloopPressed");
}
//按钮3,测试按钮
- (IBAction)buttonTestPressed:(id)sender{
NSLog(@"Enter buttonTestPressed");
NSLog(@"Exit buttonTestPressed");
}
//线程函数
BOOL threadProcessFinished =NO;
-(void)threadProce{
NSLog(@"Enter threadProce.");
threadProcessFinished =NO;
for (int i=0; i<5;i++) {
NSLog(@"InthreadProce count = %d.", i); sleep(1);
}
threadProcessFinished =YES;
NSLog(@"Exit threadProce.");
}
操作一:点击按钮1,然后立即点击按钮3,发现:只有线程结束后,才响应按钮3的事件。具体结果如下:
NSRunloopDemo[47515:645301] EnterbuttonNormalThreadTestPressed NSRunloopDemo[47515:645594] Enter threadProce. NSRunloopDemo[47515:645594] InthreadProce count = 0. NSRunloopDemo[47515:645594] InthreadProce count = 1. NSRunloopDemo[47515:645594] InthreadProce count = 2. NSRunloopDemo[47515:645594] InthreadProce count = 3. NSRunloopDemo[47515:645594] InthreadProce count = 4. NSRunloopDemo[47515:645594] Exit threadProce. NSRunloopDemo[47515:645301] ExitbuttonNormalThreadTestPressed NSRunloopDemo[47515:645301] Enter buttonTestPressed NSRunloopDemo[47515:645301] Exit buttonTestPressed
操作二:点击按钮2,然后立即点击按钮3,发现:在Runlopp等待过程才可以响应按钮3的事件。具体结果如下:
NSRunloopDemo[47748:651304] Enter buttonRunloopPressed NSRunloopDemo[47748:651523] Enter threadProce. NSRunloopDemo[47748:651523] InthreadProce count = 0. NSRunloopDemo[47748:651304] Enter buttonTestPressed NSRunloopDemo[47748:651304] Exit buttonTestPressed NSRunloopDemo[47748:651523] InthreadProce count = 1. NSRunloopDemo[47748:651523] InthreadProce count = 2. NSRunloopDemo[47748:651523] InthreadProce count = 3. NSRunloopDemo[47748:651523] InthreadProce count = 4. NSRunloopDemo[47748:651523] Exit threadProce.
2 进一步了解Runloop
通过上面的例子,我们对Runloop有了一个基本的认识,下面我们进一步的了解Runloop。
2.1 任务来源:Runloop接受来自 输入源 和 定时源 两个来源的任务。
- 输入源:投递异步消息,通常来自于另一个thread或另一个应用程序。
- 定时源:在计划的时间或重复的时间间隔内投递同步消息。
2.2 Runloop对外接口
Runloop包含5个类:CFRunLoopRef、CFRunLoopModeRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObserverRef。每个Runloop包含多个Mode,每个Mode由若干个Source、Timer、Observer组成。调用Runloop的主函数时,需要指定一个Mode作为CurrentMode。
2.3 Runloop内部逻辑:当有任务触发时,Runloop会自动处理之前未处理的消息,并通知相关的观察者。
-
通知观察者:Runloop已经启动。
-
通知观察者:即将开始处理Timer。
-
通知观察者:即将启动Source。
-
启动Source。
-
如果Source准备好并处于等待状态,立即启动并进入步骤 9。
-
通知观察者:线程即将进入休眠。
-
将线程置于休眠直到任一下面的事件发生:
a. 某一事件到达基于端口的源;
b. 定时器启动;
c. Runloop设置的时间已经超时;
d. Runloop被显式唤醒。 -
通知观察者:线程即将被唤醒。
-
处理未处理的事件
a. 如果用户定义的定时器启动,处理定时器事件并重启 Runloop。进入步骤 2。
b. 如果输入源启动,传递相应的消息。
c. 如果Runloop被显式唤醒而且时间还没超时,重启 Run loop,进入步骤 2。 -
通知观察者Run loop结束。
3 Runloop示例一
如下示例中,Runloop的输入源是一个NSTimer类型的Source,NSTimer每隔一秒给Runloop触发一次任务,由Runloop处理。
- (void)viewDidLoad
{
[super viewDidLoad];
[NSThread detachNewThreadSelector:@selector(newThreadProcess)
toTarget:self
withObject:nil];
}
- (void)newThreadProcess
{
@autoreleasepool {
//获得当前thread的Runloop
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
//设置Run loop observer的运行环境
CFRunLoopObserverContext context = {0,(__bridge void *)(self),NULL,NULL,NULL};
//创建Run loop observer对象
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if(observer)
{
//将Cocoa的NSRunLoop类型转换成CoreFoundation的CFRunLoopRef类型
CFRunLoopRef cfRunLoop = [myRunLoop getCFRunLoop];
//将新建的observer加入到当前thread的runloop
CFRunLoopAddObserver(cfRunLoop, observer, kCFRunLoopDefaultMode);
}
[NSTimer scheduledTimerWithTimeInterval: 1
target:self
selector:@selector(timerProcess)
userInfo:nil
repeats:YES];
NSInteger loopCount = 2;
do{
//启动当前thread的loop
[myRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:12.0]];
loopCount--;
}while (loopCount);
}
}
void myRunLoopObserver(CFRunLoopObserverRef observer,CFRunLoopActivity activity,void *info)
{
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"run loop entry"); break;
case kCFRunLoopBeforeTimers:
NSLog(@"run loop before timers"); break;
case kCFRunLoopBeforeSources:
NSLog(@"run loop before sources"); break;
case kCFRunLoopBeforeWaiting:
NSLog(@"run loop before waiting"); break;
case kCFRunLoopAfterWaiting:
NSLog(@"run loop after waiting"); break;
case kCFRunLoopExit:
NSLog(@"run loop exit"); break;
default:
break;
}
}
- (void)timerProcess{
for (int i=0; i<5; i++) {
NSLog(@"In timerProcess count = %d.", i);
sleep(1);
}
}
4 Runloop示例二
阻塞线程,在其他线程执行后再执行。
BOOL StopFlag =NO;
- (void)viewDidLoad
{
[super viewDidLoad];
StopFlag =NO;
[NSThread detachNewThreadSelector:@selector(newThreadProc)
toTarget:self
withObject:nil];
while (!StopFlag) {
NSLog(@"Beginrunloop");
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
NSLog(@"Endrunloop.");
}
NSLog(@"OK");
}
//方案一:等待新的线程执行完毕
//结果:在新线程执行完成后很久,Runloop才收到退出的消息
-(void)newThreadProc{
NSLog(@"Enter newThreadProc.");
for (int i=0; i<10; i++) {
NSLog(@"InnewThreadProc count = %d.", i);
sleep(1);
}
StopFlag =YES;
NSLog(@"Exit newThreadProc.");
}
//方案二:在新线程中执行完成后,通知主线程
//结果:在新线程执行完成后,Runloop直接收到退出的消息
-(void)newThreadProc{
NSLog(@"Enter newThreadProc.");
for (int i=0; i<10; i++) {
NSLog(@"InnewThreadProc count = %d.", i);
sleep(1);
}
[self performSelectorOnMainThread:@selector(setEnd)
withObject:nil
waitUntilDone: NO];
NSLog(@"Exit newThreadProc.");
}
-(void)setEnd{
StopFlag = YES;
}
方案二相比于方案一,更体现出使用Runloop的目的:有任务时执行任务,没有任务时休眠。
网友评论