使用范例
-(NSThread *)networkRequestThread{
//單例創(chuàng)建線程對(duì)象
static NSThread *networkRequestThread;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//創(chuàng)建的同時(shí)啟動(dòng)該線程runloop
networkRequestThread = [[NSThread alloc]initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
[networkRequestThread start];
});
return networkRequestThread;
}
//線程入口
-(void) networkRequestThreadEntryPoint:(id)__unused object{
//自動(dòng)釋放池,保證里面的autorelease對(duì)象能夠在出了作用范圍外就可以自動(dòng)釋放,而不是一直等到runloop結(jié)束
@autoreleasepool {
[[NSThread currentThread] setName:@"AFNetworking"];
//獲取當(dāng)前線程runloop
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
//給runloop添加一個(gè)端口作為輸入源,用于線程間通信
[currentRunloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[currentRunloop run];
}
}
使用runloop情景:
開啟一個(gè)常駐線程,能夠保持長時(shí)間去處理各種事件。
NSTimer CADisplayLink 通過向Runloop注冊(cè)來開啟定時(shí)任務(wù)
GCD的延時(shí)操作,UI界面刷新,響應(yīng)用戶的觸摸事件
addPort:{NSMMachPort port】這里將輸入源端口添加到runloop里,通過輸入源端口相當(dāng)于一個(gè)載體,用于進(jìn)行線程間消息的傳遞。
線程間通信方式:通過NSMachPort端口
將端口添加到當(dāng)前線程的runloop里,在runloop的消息循環(huán)機(jī)制中去接受通過端口傳遞過來的消息。也可以通過端口去給其他線程發(fā)送消息。
RunLoop只能運(yùn)行在一種mode下,如果要換mode,當(dāng)前的loop也需要停下重啟成新的。利用這個(gè)機(jī)制,ScrollView滾動(dòng)過程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode會(huì)切換到UITrackingRunLoopMode來保證ScrollView的流暢滑動(dòng):只能在NSDefaultRunLoopMode模式下處理的事件會(huì)影響scrllView的滑動(dòng)。
如果我們把一個(gè)NSTimer對(duì)象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運(yùn)行循環(huán)中的時(shí)候, ScrollView滾動(dòng)過程中會(huì)因?yàn)閙ode的切換,而導(dǎo)致NSTimer將不再被調(diào)度。
同時(shí)因?yàn)閙ode還是可定制的,所以:
Timer計(jì)時(shí)會(huì)被scrollView的滑動(dòng)影響的問題可以通過將timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)來解決。
1、如果是在主線程中運(yùn)行timer,想要timer在某界面有視圖滾動(dòng)時(shí),依然能正常運(yùn)轉(zhuǎn),那么將timer添加到RunLoop中時(shí),就需要設(shè)置mode 為NSRunLoopCommonModes。
2、如果是在子線程中運(yùn)行timer,那么將timer添加到RunLoop中后,Mode設(shè)置為NSDefaultRunLoopMode或NSRunLoopCommonModes均可,但是需要保證RunLoop在運(yùn)行,且其中有任務(wù)。
偽代碼
function loop() {
initialize();
do {
var message = get_next_message();
process_message(message);
} while (message != quit);
}
CADisplayLink是一個(gè)能讓我們以和屏幕刷新率同步的頻率將特定的內(nèi)容畫到屏幕上的定時(shí)器類
CADisplayLink以特定模式注冊(cè)到runloop后,每當(dāng)屏幕顯示內(nèi)容刷新結(jié)束的時(shí)候,runloop就會(huì)向CADisplayLink指定的target發(fā)送一次指定的selector消息,CADisplayLink類對(duì)應(yīng)的selector就會(huì)被調(diào)用一次。
22.runloop和線程有什么關(guān)系
runloop與線程是一一對(duì)應(yīng)的
對(duì)于主線程來說,runloop在默認(rèn)啟動(dòng)的。
對(duì)于子線程來說,runloop是懶加載的,只有當(dāng)我們手動(dòng)獲取的時(shí)候才會(huì)創(chuàng)建。
當(dāng)線程的 runloop 被打開后就能保持著接受并處理各種消息,而不會(huì)中途退出(有任務(wù)就去接受處理,沒有就處于休眠狀態(tài))。
23.runloop的mode作用是什么
model 按照優(yōu)先級(jí)可分為:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認(rèn),通常主線程在這個(gè) Mode 下運(yùn)行
UITrackingRunLoopMode:保證Scrollview滑動(dòng)順暢
UIInitializationRunLoopMode:?jiǎn)?dòng)程序后的過渡mode,啟動(dòng)完成后就不再使用
NSRunLoopCommonModes :占位mode,可以向其中添加其他mode
24.以+ scheduledTimerWithTimeInterval...的方式觸發(fā)的timer,在滑動(dòng)頁面上的列表時(shí),timer會(huì)暫定回調(diào),為什么?如何解決
(RunLoop只能運(yùn)行在一種mode下,切換mode的時(shí)候runloop會(huì)有一個(gè)重啟的動(dòng)作。
ScrollView滾動(dòng)過程中NSDefaultRunLoopMode的mode會(huì)切換到UITrackingRunLoopMode來保證ScrollView的流暢滑動(dòng)。NSTimer 對(duì)象默認(rèn)是以NSDefaultRunLoopMode注冊(cè)到runloop里的,所以切換的時(shí)候?qū)е翹STimer將不再被調(diào)度。所以需要將NSTimer以 NSRunLoopCommonModes 的方式注冊(cè)到Runloop中才能保持一致調(diào)用。)
RunLoop 理解成下面循環(huán)
1.休眠狀態(tài)
2.接收到要處理的消息,被喚醒
3.對(duì)消息進(jìn)行處理
4.消息處理完畢之后,回到休眠狀態(tài)
1(等待消息) -> 2(將要處理消息) -> 3(處理消息) -> 4(消息處理完成) -> 1(等待消息)
對(duì)于每一個(gè) runloop 系統(tǒng)會(huì)默認(rèn)為期創(chuàng)建一個(gè)自動(dòng)釋放池。
AutoreleasePool 在 4 (消息處理完成) 這個(gè)階段進(jìn)行釋放。iOS 開發(fā)者寫的程序,無論包含多少層函數(shù)調(diào)用,也一直處于 3 (處理消息) 這個(gè)階段當(dāng)中,其余階段屬于 UI 框架的一部分。因此在當(dāng)前消息循環(huán)內(nèi),放到 AutoreleasePool 的對(duì)象會(huì)一直生效,并不會(huì)被釋放
入口函數(shù)作用
- 初始化UIApplication單例對(duì)象
-?初始化AppDelegate對(duì)象,并設(shè)為UIApplication對(duì)象的代理
-?建立一個(gè)主事件循環(huán),其中包含UIApplication的Runloop來開始處理事件。
runloop退出的條件:app退出;線程關(guān)閉;
當(dāng)我們?nèi)?dòng) RunLoop,系統(tǒng)會(huì)自動(dòng)在內(nèi)部創(chuàng)建自動(dòng)釋放池。