- 版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。
一. Runloop的基本知識(shí)
1. 概念
Runloop是運(yùn)動(dòng)循環(huán),不斷跑圈,無限循環(huán).
RunLoop 是 iOS 和 OSX 開發(fā)中非?;A(chǔ)的一個(gè)概念,這篇文章將從 CFRunLoop 的源碼入手,介紹 RunLoop 的概念以及底層實(shí)現(xiàn)原理。之后會(huì)介紹一下在 iOS 中,蘋果是如何利用 RunLoop 實(shí)現(xiàn)自動(dòng)釋放池、延遲回調(diào)、觸摸事件、屏幕刷新等功能的。
- 作用:
保持程序的持續(xù)運(yùn)行 (iOS程序一直活著的原因)
處理App中的各種事件(eg:觸摸事件/定時(shí)器事件/selector事件[選擇器·performSelector···])
節(jié)省CPU資源,提高程序的性能(有事做事,沒事休息)
程序已啟動(dòng),就開啟了一個(gè)runloop無限循環(huán),因此程序才能持續(xù)的運(yùn)行

2.RunLoop對(duì)象
- 在iOS開發(fā)中有兩套api來訪問Runloop
Foundation框架 [NSRunloop](OC)
Core Foundation框架 [CFRunloopRef](C)```
2. NSRunLoop和CFRunLoopRef都代表著RunLoop對(duì)象,它們是等價(jià)的,可以互相轉(zhuǎn)換
3. NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API(Core Foundation層面)
######3.Runloop與線程的關(guān)系
1.Runloop和線程的關(guān)系:一個(gè)Runloop對(duì)應(yīng)著一條唯一的線程
問題:如何讓子線程不死
回答:給這條子線程開啟一個(gè)Runloop
2.Runloop的創(chuàng)建:主線程Runloop已經(jīng)創(chuàng)建好了,子線程的runloop需要手動(dòng)創(chuàng)建
3.Runloop的生命周期:在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀
###二. Runloop對(duì)象的創(chuàng)建
1. Foundation框架\[NSRunloop]創(chuàng)建Runloop對(duì)象
// 獲取當(dāng)前線程下的Runloop, 懶加載的形式創(chuàng)建
NSRunLoop * runloop1 = [NSRunLoop currentRunLoop];
// 獲取主線程下的Runloop
NSRunLoop * runloop1 = [NSRunLoop mainRunLoop];```
- Core Foundation框架[CFRunloopRef]創(chuàng)建Runloop對(duì)象
// 獲取當(dāng)前線程下的Runloop, 懶加載的形式創(chuàng)建
CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
// 獲取主線程下的Runloop
CFRunLoopRef runloop2 = CFRunLoopGetMain();```
3. Runloop運(yùn)行原理圖

4.Runloop相關(guān)的類
CFRunLoopRef
CFRunloopModeRef [Runloop的運(yùn)行模式]
CFRunloopSourceRef [Runloop要處理的事件源]
CFRunloopTimerRef [Timer事件]
CFRunloopObserverRef [Runloop的觀察者(監(jiān)聽者)]```
Runloop要想跑起來,它的內(nèi)部必須要有一個(gè)mode,mode中必須有source/observer/time,至少要有其中的一個(gè).

5.CFRunloopModeRef [Runloop的五個(gè)運(yùn)行模式]
Runloop每次啟動(dòng)的時(shí)候只能指定一個(gè)運(yùn)行模型,切換模式時(shí)必須退出當(dāng)前的Runloop,再重新進(jìn)入一個(gè)mode,是為了分割不同組的定時(shí)器互不影響
kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線程是在這個(gè)Mode下運(yùn)行
UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響
UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用
GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到
kCFRunLoopCommonModes: 這是一個(gè)占位用的Mode,不是一種真正的Mode
6.CFRunloopTimerRef [Timer事件]
-
1.Timer事件
- 2.GCD定時(shí)器
GCD定時(shí)器必須必須保存起來才能使用
- (void)gcdTimer
{
NSLog(@"+++++++++");
// 1.創(chuàng)建定時(shí)器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
// 2.設(shè)置定時(shí)器
/*
第一個(gè)參數(shù):定時(shí)器
第二個(gè)參數(shù):從哪個(gè)時(shí)間開始
第三個(gè)參數(shù):間隔時(shí)間
第四個(gè)參數(shù):精確度, 0代表無誤差
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
// 3.定時(shí)器觸發(fā)事件
dispatch_source_set_event_handler(timer, ^{
NSLog(@"----------");
});
// 4.開啟定時(shí)器
dispatch_resume(timer);
// GCD定時(shí)器創(chuàng)建是個(gè)局部變量需要保存才能執(zhí)行
self.timer = timer;
}```
- 3.CFRunloopSourceRef [Runloop要處理的事件源]
(1)以前的分法
```swift
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources```
(2)現(xiàn)在的分法
```swift
Source0:非基于Port的 (用戶觸發(fā)的時(shí)間)
Source1:基于Port的 (通過內(nèi)核和其它線程相互發(fā)送消息)```
- 4.CFRunloopObserverRef [Runloop的觀察者 (監(jiān)聽者)]

注意:

- 5.Runloop運(yùn)行邏輯


###iOS中的RunLoop
>Runloop是事件接收和分發(fā)機(jī)制的一個(gè)實(shí)現(xiàn)。
Runloop提供了一種異步執(zhí)行代碼的機(jī)制,不能并行執(zhí)行任務(wù)。
在主隊(duì)列中,Main RunLoop直接配合任務(wù)的執(zhí)行,負(fù)責(zé)處理UI事件、定時(shí)器以及其他內(nèi)核相關(guān)事件。
>RunLoop的主要目的:
保證程序執(zhí)行的線程不會(huì)被系統(tǒng)終止。
>什么時(shí)候使用RunLoop ?
當(dāng)需要和該線程進(jìn)行交互的時(shí)候才會(huì)使用Runloop.
每一個(gè)線程都有其對(duì)應(yīng)的RunLoop,但是默認(rèn)非主線程的RunLoop是沒有運(yùn)行的,需要為RunLoop添加至少一個(gè)事件源,然后去run它。
一般情況下我們是沒有必要去啟用線程的RunLoop的,除非你在一個(gè)單獨(dú)的線程中需要長(zhǎng)久的檢測(cè)某個(gè)事件。
主線程??默認(rèn)有Runloop。當(dāng)自己?jiǎn)?dòng)一個(gè)線程,如果只是用于處理單一的事件,則該線程在執(zhí)行完之后就退出了。所以當(dāng)我們需要讓該線程監(jiān)聽某項(xiàng)事務(wù)時(shí),就得讓線程一直不退出,runloop就是這么一個(gè)循環(huán),沒有事件的時(shí)候,一直卡著,有事件來臨了,執(zhí)行其對(duì)應(yīng)的函數(shù)。
Runloop,正如其名所示,是線程進(jìn)入和被線程用來響應(yīng)事件以及調(diào)用事件處理函數(shù)的地方。需要在代碼中使用控制語句實(shí)現(xiàn)run loop的循環(huán),也就是說,需要代碼提供while 或者 for循環(huán)來驅(qū)動(dòng)run loop。
在這個(gè)循環(huán)中,使用一個(gè)Runloop對(duì)象[NSRunloop currentRunloop]執(zhí)行接收消息,調(diào)用對(duì)應(yīng)的處理函數(shù)。
Runloop接收兩種源事件:input sources和timer sources。
input sources 傳遞異步事件,通常是來自其他線程和不同的程序中的消息;
timer sources(定時(shí)器) 傳遞同步事件(重復(fù)執(zhí)行或者在特定時(shí)間上觸發(fā))。
除了處理input sources,Runloop 也會(huì)產(chǎn)生一些關(guān)于本身行為的notificaiton。注冊(cè)成為Runloop的observer,可以接收到這些notification,做一些額外的處理。(使用CoreFoundation來成為runloop的observer)。
>Runloop工作的特點(diǎn):
1> 當(dāng)有事件發(fā)生時(shí),Runloop會(huì)根據(jù)具體的事件類型通知應(yīng)用程序作出響應(yīng);
2> 當(dāng)沒有事件發(fā)生時(shí),Runloop會(huì)進(jìn)入休眠狀態(tài),從而達(dá)到省電的目的;
3> 當(dāng)事件再次發(fā)生時(shí),Runloop會(huì)被重新喚醒,處理事件。
>提示:一般在開發(fā)中很少會(huì)主動(dòng)創(chuàng)建Runloop,而通常會(huì)把事件添加到Runloop中。
####參考:
[官方文檔](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW47)
[深入理解RunLoop](http://blog.ibireme.com/2015/05/18/runloop/)
