Runloop學(xué)習(xí)
| 目錄 |
|: ------------- |
| 1 什么是Runloop嗓奢? |
| 2 進(jìn)一步了解Runloop|
| 3 Runloop示例一 |
| 4 Runloop示例二 |
1 什么是Runloop?
一個(gè)Runloop就是一個(gè)處理事件的循環(huán),通過Runloop可以讓線程在沒有任務(wù)時(shí)處于睡眠狀態(tài)幔妨,當(dāng)有任務(wù)觸發(fā)時(shí)易遣,也可以立即處理先巴。這里褐奥,從一個(gè)簡(jiǎn)單的例子出發(fā),看看Runloop的使用饭于。
//按鈕1蜀踏,以睡眠的方式保證線程持續(xù)運(yùn)行
- (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的方式保證線程持續(xù)運(yùn)行
- (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,測(cè)試按鈕
- (IBAction)buttonTestPressed:(id)sender{
NSLog(@"Enter buttonTestPressed");
NSLog(@"Exit buttonTestPressed");
}
//線程函數(shù)
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.");
}
操作一:點(diǎn)擊按鈕1果覆,然后立即點(diǎn)擊按鈕3颅痊,發(fā)現(xiàn):只有線程結(jié)束后,才響應(yīng)按鈕3的事件局待。具體結(jié)果如下:
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
操作二:點(diǎn)擊按鈕2斑响,然后立即點(diǎn)擊按鈕3,發(fā)現(xiàn):在Runlopp等待過程才可以響應(yīng)按鈕3的事件钳榨。具體結(jié)果如下:
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 進(jìn)一步了解Runloop
通過上面的例子舰罚,我們對(duì)Runloop有了一個(gè)基本的認(rèn)識(shí),下面我們進(jìn)一步的了解Runloop重绷。
2.1 任務(wù)來(lái)源:Runloop接受來(lái)自 輸入源 和 定時(shí)源 兩個(gè)來(lái)源的任務(wù)沸停。
- 輸入源:投遞異步消息,通常來(lái)自于另一個(gè)thread或另一個(gè)應(yīng)用程序昭卓。
- 定時(shí)源:在計(jì)劃的時(shí)間或重復(fù)的時(shí)間間隔內(nèi)投遞同步消息。
2.2 Runloop對(duì)外接口
Runloop包含5個(gè)類:CFRunLoopRef瘟滨、CFRunLoopModeRef候醒、CFRunLoopSourceRef、CFRunLoopTimerRef杂瘸、CFRunLoopObserverRef倒淫。每個(gè)Runloop包含多個(gè)Mode,每個(gè)Mode由若干個(gè)Source败玉、Timer敌土、Observer組成。調(diào)用Runloop的主函數(shù)時(shí)运翼,需要指定一個(gè)Mode作為CurrentMode返干。
2.3 Runloop內(nèi)部邏輯:當(dāng)有任務(wù)觸發(fā)時(shí),Runloop會(huì)自動(dòng)處理之前未處理的消息血淌,并通知相關(guān)的觀察者矩欠。
通知觀察者:Runloop已經(jīng)啟動(dòng)。
通知觀察者:即將開始處理Timer悠夯。
通知觀察者:即將啟動(dòng)Source癌淮。
啟動(dòng)Source。
如果Source準(zhǔn)備好并處于等待狀態(tài)沦补,立即啟動(dòng)并進(jìn)入步驟 9乳蓄。
通知觀察者:線程即將進(jìn)入休眠。
-
將線程置于休眠直到任一下面的事件發(fā)生:
a. 某一事件到達(dá)基于端口的源夕膀;
b. 定時(shí)器啟動(dòng)虚倒;
c. Runloop設(shè)置的時(shí)間已經(jīng)超時(shí)美侦;
d. Runloop被顯式喚醒。 通知觀察者:線程即將被喚醒裹刮。
-
處理未處理的事件
a. 如果用戶定義的定時(shí)器啟動(dòng),處理定時(shí)器事件并重啟 Runloop音榜。進(jìn)入步驟 2。
b. 如果輸入源啟動(dòng),傳遞相應(yīng)的消息捧弃。
c. 如果Runloop被顯式喚醒而且時(shí)間還沒超時(shí),重啟 Run loop赠叼,進(jìn)入步驟 2。 通知觀察者Run loop結(jié)束违霞。
3 Runloop示例一
如下示例中嘴办,Runloop的輸入源是一個(gè)NSTimer類型的Source,NSTimer每隔一秒給Runloop觸發(fā)一次任務(wù)买鸽,由Runloop處理涧郊。
- (void)viewDidLoad
{
[super viewDidLoad];
[NSThread detachNewThreadSelector:@selector(newThreadProcess)
toTarget:self
withObject:nil];
}
- (void)newThreadProcess
{
@autoreleasepool {
//獲得當(dāng)前thread的Runloop
NSRunLoop* myRunLoop = [NSRunLoop currentRunLoop];
//設(shè)置Run loop observer的運(yùn)行環(huán)境
CFRunLoopObserverContext context = {0,(__bridge void *)(self),NULL,NULL,NULL};
//創(chuàng)建Run loop observer對(duì)象
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,kCFRunLoopAllActivities, YES, 0, &myRunLoopObserver, &context);
if(observer)
{
//將Cocoa的NSRunLoop類型轉(zhuǎn)換成CoreFoundation的CFRunLoopRef類型
CFRunLoopRef cfRunLoop = [myRunLoop getCFRunLoop];
//將新建的observer加入到當(dāng)前thread的runloop
CFRunLoopAddObserver(cfRunLoop, observer, kCFRunLoopDefaultMode);
}
[NSTimer scheduledTimerWithTimeInterval: 1
target:self
selector:@selector(timerProcess)
userInfo:nil
repeats:YES];
NSInteger loopCount = 2;
do{
//啟動(dòng)當(dāng)前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示例二
阻塞線程,在其他線程執(zhí)行后再執(zhí)行眼五。
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");
}
//方案一:等待新的線程執(zhí)行完畢
//結(jié)果:在新線程執(zhí)行完成后很久妆艘,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.");
}
//方案二:在新線程中執(zhí)行完成后,通知主線程
//結(jié)果:在新線程執(zhí)行完成后看幼,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;
}
方案二相比于方案一批旺,更體現(xiàn)出使用Runloop的目的:有任務(wù)時(shí)執(zhí)行任務(wù),沒有任務(wù)時(shí)休眠诵姜。