什么是RunLoop
RunLoop即消息機(jī)制的處理模式躲胳。NSRunLoop的作用在于有事情做的時(shí)候使的當(dāng)前NSRunLoop的線程工作,沒有事情做讓當(dāng)前NSRunLoop的線程休眠首昔。
主要職責(zé):
1.保證程序的持續(xù)運(yùn)行并接受用戶的輸入叶组。
2.節(jié)省CPU時(shí)間,即有任務(wù)的時(shí)候就干活圆仔,沒任務(wù)的話就休息
3.去決定程序在什么時(shí)候去處理一些Event
4.調(diào)用解耦(Message Queue)
誰依賴NSRunloop
NSTimer
UIEvent
autorelease
NSObject(NSDelaydPerforming)
NSObject(NSThreadPerformAddtion)
CADisplayLink
CATransition
CAAnimation
dispatch_get_main_queue()
等垃瞧。
/*-------------啟動runLoop------------------*//*
?通過[NSRunLoop currentRunLoop]或者? CFRunLoopGetCurrent()方式可以獲取到當(dāng)前線程的runLoop。根據(jù)蘋果公司的文檔坪郭,啟動一個(gè)runLoop有以下三種方法:
?- (void)run;
?- (void)runUntilDate:(NSDate *)limitDate;?
- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;
1.使用第一種方式runLoop會一直運(yùn)行下去个从,在此期間會處理來自輸入源的數(shù)據(jù),并且會在NSDefaultRunLoopMode模式下重復(fù)調(diào)用 - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate方法?
2.使用第二種啟動方式歪沃,可以設(shè)置超時(shí)時(shí)間嗦锐,在超時(shí)時(shí)間到達(dá)之前,runLoop會一直運(yùn)行沪曙,在此期間runLoop會處理來自輸入源的數(shù)據(jù)奕污,并且也會在NSDefaultRunLoopMode模式下重復(fù)調(diào)用 - (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate;方法?
3.使用第三種方法runLoop會運(yùn)行一次,超時(shí)時(shí)間到達(dá)或者一個(gè)輸入源被處理液走,則runLoop就會自動退出 */? ?
?? ? /*-----------------退出runLoop---------------------*/
/* 這是較好退出方式 第三種啟動方式runMode:beforeDate: 通過這種方式啟動碳默,runloop會運(yùn)行一次,當(dāng)超時(shí)時(shí)間到達(dá)或者第一個(gè)輸入源被處理嘱根,runloop就會退出该抒。測試代碼如下:
?- (void)createRunLoopInNewThread {?
// 注冊runloop觀察者?
static CFRunLoopObserverRef _observer; RegisterRunLoopObserver(kCFRunLoopAllActivities, _observer, 0, kCFRunLoopDefaultMode, (__bridge void*)self, RunLoopCallBack);?
?_theRL = [NSRunLoop currentRunLoop];?
?_port = (NSMachPort *)[NSMachPort port]; [_theRL addPort:_port forMode:NSDefaultRunLoopMode];?
?[_theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];? // 如果當(dāng)前線程的runloop沒有退出欧引,則`[_theRL run]`之后的代碼不會執(zhí)行. NSLog(@"runloop已退出"); //只有當(dāng)runloop退出,這里才會執(zhí)行瞬哼。可以通過注冊runloop觀察者進(jìn)行驗(yàn)證,這里就不貼代碼了,具體代碼請到demo里查看。 }??
#pragma mark - Touch?
?- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {?
UIColor *color = self.view.backgroundColor;?
self.view.backgroundColor = color == [UIColor redColor] ? [UIColor yellowColor] : [UIColor redColor];?
?// 線程間的通信 (這里是main thread) [self performSelector:@selector(communicateToNewThreadFromMainThread) onThread:_thread withObject:nil waitUntilDone:NO]; }?
?- (void)communicateToNewThreadFromMainThread { NSLog(@"communicate successfully "); //這里是com.xindong.thread. 這里執(zhí)行完排惨,表示第一個(gè)輸入源事件被處理. } 當(dāng)我們觸摸屏幕時(shí),communicateToNewThreadFromMainThread方法被執(zhí)行匾效,即輸入源事件被處理,然后runloop退出。如果我們想控制runloop的退出時(shí)機(jī)虎敦,而不是在處理完一個(gè)輸入源事件之后就退出,那么就要重復(fù)調(diào)用runMode:beforeDate:政敢,
具體可以參考蘋果文檔給出的方案其徙,如下:
?BOOL shouldKeepRunning = YES;
?// global NSRunLoop *theRL = [NSRunLoop currentRunLoop];
?while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);?
接著我們對代碼進(jìn)行修改,以便我們可以控制runloop的退出時(shí)機(jī)喷户,改后如下:
?- (void)createRunLoopInNewThread {
?// 注冊runloop觀察者 static CFRunLoopObserverRef _observer; RegisterRunLoopObserver(kCFRunLoopAllActivities, _observer, 0, kCFRunLoopDefaultMode, (__bridge void*)self, RunLoopCallBack);?
?_theRL = [NSRunLoop currentRunLoop];?
?_port = (NSMachPort *)[NSMachPort port]; [_theRL addPort:_port forMode:NSDefaultRunLoopMode];??
while (shouldKeepRunning && [_theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);? // 如果當(dāng)前線程的runloop沒有退出唾那,則`[_theRL run]`之后的代碼不會執(zhí)行. NSLog(@"runloop已退出"); //只有當(dāng)runloop退出,這里才會執(zhí)行褪尝∧只瘢可以通過注冊runloop觀察者進(jìn)行驗(yàn)證,這里就不貼代碼了河哑,具體代碼請到demo里查看避诽。 }??
#pragma mark - Touch? - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent *)event {
UIColor *color = self.view.backgroundColor;
self.view.backgroundColor = color == [UIColor redColor] ? [UIColor yellowColor] : [UIColor redColor];
// 線程間的通信 (這里是main thread)
[self performSelector:@selector(communicateToNewThreadFromMainThread) onThread:_thread withObject:nil waitUntilDone:NO];
}
- (void)communicateToNewThreadFromMainThread {
NSLog(@"communicate successfully "); //這里是com.xindong.thread
[self quitRunLoop];
}
- (void)quitRunLoop {
shouldKeepRunning = NO;
CFRunLoopStop(CFRunLoopGetCurrent());
}
通過上述方式啟動和退出runloop,沒有引起內(nèi)存泄漏璃谨,也沒有造成內(nèi)存增長茎用,并且對runloop的退出時(shí)機(jī)可以自由控制。相對來說睬罗,使用此方案更好一些轨功。
*/
/*
注意
RunLoop在同一段時(shí)間只能且必須在一種特定Mode下Run。
更換Mode時(shí)容达,需要停止當(dāng)前Loop古涧,然后重啟新Mode。
Mode是iOS滑動順暢的關(guān)鍵花盐。
Mode中有三個(gè)非常重要的組成部分羡滑,Timer(定時(shí)器)、 Source(事件源) 以及Observor(觀察者)算芯。一個(gè) RunLoop 包含若干個(gè) Mode柒昏,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer。首先要指出的是一個(gè)runloop啟動時(shí)必須指定一個(gè)Mode? , 并且這個(gè)Mode被稱為currentMode 熙揍。如果要切換Mode,只能退出runloop重新進(jìn)入职祷。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。
類型
NSDefaultRunLoopMode
默認(rèn)狀態(tài)(空閑狀態(tài))有梆,比如點(diǎn)擊按鈕都是這個(gè)狀態(tài)
UITrackingRunLoopMode
滑動時(shí)的Mode是尖。比如滑動UIScrollView時(shí)。
UIInitializationRunLoopMode
私有的泥耀,APP啟動時(shí)饺汹。就是從iphone桌面點(diǎn)擊APP的圖標(biāo)進(jìn)入APP到第一個(gè)界面展示之前,在第一個(gè)界面顯示出來后痰催,UIInitializationRunLoopMode就被切換成了NSDefaultRunLoopMode兜辞。
NSRunLoopCommonModes
它是NSDefaultRunLoopMode和UITrackingRunLoopMode的集合。結(jié)構(gòu)類似于一個(gè)數(shù)組夸溶。在這個(gè)mode下執(zhí)行其實(shí)就是兩個(gè)mode都能執(zhí)行而已弦疮。
典型的應(yīng)用場景這樣:當(dāng)前界面有開啟一個(gè)NSTimer,并且滑動UIScrollView蜘醋。正常開啟NSTimer后胁塞,滑動UIScrollView時(shí)它是不滑動的。解決辦法就是把這個(gè)timer加入到當(dāng)前的RunLoop压语,并把RunLoop的mode設(shè)置為NSRunLoopCommonModes啸罢。這樣就可以保證不管你是NSDefaultRunLoopMode里跑,還是UITrackingRunLoopMode里跑胎食,這個(gè)timer都可以執(zhí)行扰才。
RunLoop和GCD的關(guān)系
RunLoop和GCD的關(guān)系,準(zhǔn)確來說是只要使用了dispatch_get_main_queue()厕怜,就與RunLoop有了關(guān)系衩匣。
NSRunloop與程序運(yùn)行
程序的入口——main.m文件,一個(gè)ios程序啟動后粥航,只有短短的十行代碼居然能保持整個(gè)應(yīng)用程序一直運(yùn)行而沒有退出琅捏,是不是有點(diǎn)意思?程序之所以沒有直接退出是因?yàn)閁IApplicationMain這個(gè)函數(shù)內(nèi)部默認(rèn)啟動了一個(gè)跟主線程相關(guān)的NSRunloop對象递雀,而UIApplicationMain這個(gè)函數(shù)一直執(zhí)行沒有返回就保存程序一直運(yùn)行的狀態(tài)柄延。
-------------總結(jié)-----------------
如果不想退出runLoop可以使用第一種啟動方法;如果使用第二種方式啟動runLoop缀程,可以通過設(shè)置超時(shí)時(shí)間來退出搜吧;如果使用第三種方式啟動runLoop,可以通過設(shè)置超時(shí)時(shí)間或者使用CFRunLoopStop方法來退出
*/