深入理解RunLoop:http://www.cocoachina.com/ios/20150601/11970.html
iOS學(xué)習(xí)之RunLoop的深入理解:http://blog.csdn.net/zhonggaorong/article/details/52313740
課件:http://www.cnblogs.com/cqb-learner/p/5859431.html? ? http://www.cnblogs.com/cqb-learner/p/5860084.html
iOS Runloop學(xué)習(xí):http://www.cocoachina.com/ios/20160307/15590.html
iOS RunLoop的基本用法:http://blog.csdn.net/qcx321/article/details/53217502
RunLoop更強(qiáng)大的地方在于對(duì)消息的監(jiān)聽,因?yàn)镃FRunLoopRef的線程安全優(yōu)勢(shì),我們通常會(huì)更多使用后者。
細(xì)心的你可能會(huì)發(fā)現(xiàn)瑟啃,輸入源被注冊(cè)進(jìn)Runloop中時(shí)會(huì)有方法進(jìn)行remove,但是定時(shí)器卻沒有眼坏,但是定時(shí)器中的invalidate方法可以將其從runloop中移除沦童,正如官方文檔的說(shuō)明:invalidate是重要也是唯一的可以將定時(shí)器從runloop的注銷的方法境蜕,所以如果我們創(chuàng)建了定時(shí)器谊惭,就一定要在不使用時(shí)調(diào)用invalidate方法汽馋。我不知道apple為何將定時(shí)器的方法分離開來(lái)侮东,可能的原因是讓開發(fā)者更少的顯式調(diào)用runloop的方法圈盔,你若是知道原因,懇請(qǐng)留言指導(dǎo)悄雅。
一.RunLoop基本概念
概念:程序的運(yùn)行循環(huán),通俗的來(lái)說(shuō)就是跑圈.
1. 基本作用(作用重大)
(1) 保持程序的持續(xù)運(yùn)行(iOS程序?yàn)槭裁茨芤恢被钪粫?huì)死)
(2) 處理app中的各種事件(比如觸摸事件驱敲、定時(shí)器事件【NSTimer】、selector事件【選擇器·performSelector···】)
(3)節(jié)省CPU資源宽闲,提高程序性能众眨,有事情就做事情,沒事情就休息
2. 重要說(shuō)明
(1)如果沒有Runloop,那么程序一啟動(dòng)就會(huì)退出容诬,什么事情都做不了娩梨。
(2)如果有了Runloop,那么相當(dāng)于在內(nèi)部有一個(gè)死循環(huán)览徒,能夠保證程序的持續(xù)運(yùn)行
3.main函數(shù)中的Runloop
(1) 在UIApplication函數(shù)內(nèi)部就啟動(dòng)了一個(gè)Runloop,該函數(shù)返回一個(gè)int類型的值
(2) 這個(gè)默認(rèn)啟動(dòng)的Runloop是跟主線程相關(guān)聯(lián)的
4.Runloop對(duì)象
(1)在iOS開發(fā)中有兩套api來(lái)訪問(wèn)Runloop
第一種:foundation框架【NSRunloop】
第二種:core foundation框架【CFRunloopRef】
(2)NSRunLoop和CFRunLoopRef都代表著RunLoop對(duì)象,它們是等價(jià)的狈定,可以互相轉(zhuǎn)換
(3)NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究CFRunLoopRef層面的API(Core Foundation層面)
5.Runloop與線程
(1) Runloop和線程的關(guān)系:一個(gè)Runloop對(duì)應(yīng)著一條唯一的線程
問(wèn)題:如何讓子線程不死
回答:給這條子線程開啟一個(gè)Runloop
(2) Runloop的創(chuàng)建:主線程Runloop已經(jīng)創(chuàng)建好了纽什,子線程的runloop需要手動(dòng)創(chuàng)建
(3) Runloop的生命周期:在第一次獲取時(shí)創(chuàng)建措嵌,在線程結(jié)束時(shí)銷毀
(4) 拿到當(dāng)前應(yīng)用程序的主Runloop(主線程對(duì)應(yīng)的Runloop)
方法一: NSRunloop
NSRunLoop * runloop1=[NSRunLoop mainRunLoop];
方法二: CFRunLoopRef
CFRunLoopRef runloop2=CFRunLoopGetMain();
(5)注意點(diǎn):開一個(gè)子線程創(chuàng)建runloop,不是通過(guò)alloc init方法創(chuàng)建,而是直接通過(guò)調(diào)用currentRunLoop方法來(lái)創(chuàng)建芦缰,它本身是一個(gè)懶加載的企巢。
1.蘋果官方的運(yùn)行原理圖
2.Runloop和相關(guān)類之間的關(guān)系圖
RunLoop五個(gè)相關(guān)的類
CFRunloopRef
CFRunloopModeRef【Runloop的運(yùn)行模式】
CFRunloopSourceRef【Runloop要處理的事件源】
CFRunloopTimerRef【Timer事件】
CFRunloopObserverRef【Runloop的觀察者(監(jiān)聽者)】
以下是五個(gè)相關(guān)類的拋析:
CFRunloopRef
(1) CFRunloopModeRef代表著Runloop的運(yùn)行模式
(2) 一個(gè)Runloop中可以有多個(gè)mode,一個(gè)mode里面又可以有多個(gè)source\observer\timer等等
(3) 每次runloop啟動(dòng)的時(shí)候,只能指定一個(gè)mode,這個(gè)mode被稱為該Runloop的當(dāng)前mode
(4) 如果需要切換mode,只能先退出當(dāng)前Runloop,再重新指定一個(gè)mode進(jìn)入
(5) 這樣做主要是為了分割不同組的定時(shí)器等让蕾,讓他們相互之間不受影響
(6) 系統(tǒng)默認(rèn)注冊(cè)了5個(gè)mode
第一種模式: 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
CFRunloopTimerRef
(1) runloop一啟動(dòng)就會(huì)選中一種模式萌抵,當(dāng)選中了一種模式之后其它的模式就不會(huì)參與。一個(gè)mode里面可以添加多個(gè)NSTimer,也就是說(shuō)以后當(dāng)創(chuàng)建NSTimer的時(shí)候元镀,可以指定它是在什么模式下運(yùn)行的绍填。
(2) 它是基于時(shí)間的觸發(fā)器,說(shuō)直白點(diǎn)那就是時(shí)間到了我就觸發(fā)一個(gè)事件栖疑,觸發(fā)一個(gè)操作讨永。基本上說(shuō)的就是NSTimer .
(3) 相關(guān)代碼 / - (void)timer2 { //NSTimer 調(diào)用了scheduledTimer方法遇革,那么會(huì)自動(dòng)添加到當(dāng)前的runloop里面去卿闹,而且runloop的運(yùn)行模式kCFRunLoopDefaultMode
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0target:selfselector:@selector(run) userInfo:nilrepeats:YES];[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
CFRunloopSourceRef
(1)是事件源也就是輸入源,有兩種分類模式萝快;
一種是按照蘋果官方文檔進(jìn)行劃分的
另一種是基于函數(shù)的調(diào)用棧來(lái)進(jìn)行劃分的(source0和source1)锻霎。
(2)具體的分類情況
*以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources
*現(xiàn)在的分法
Source0:非基于Port的
Source1:基于Port的
(3) 可以通過(guò)打斷點(diǎn)的方式查看一個(gè)方法的函數(shù)調(diào)用棧
CFRunLoopObserverRef
(1) CFRunLoopObserverRef是觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變
(2) 如何監(jiān)聽
//創(chuàng)建一個(gè)runloop監(jiān)聽者
CFRunLoopObserverRef observer =
CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(),kCFRunLoopAllActivities,
YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
NSLog(@”監(jiān)聽runloop狀態(tài)改變—%zd”,activity); });
?//為runloop添加一個(gè)監(jiān)聽者?
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer,
kCFRunLoopDefaultMode);?
CFRelease(observer);
(3)監(jiān)聽的狀態(tài)
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), //即將進(jìn)入Runloop
kCFRunLoopBeforeTimers = (1UL << 1), //即將處理NSTimer
kCFRunLoopBeforeSources = (1UL << 2), //即將處理Sources
kCFRunLoopBeforeWaiting = (1UL << 5), //即將進(jìn)入休眠
kCFRunLoopAfterWaiting = (1UL << 6), //剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), //即將退出runloop
kCFRunLoopAllActivities = 0x0FFFFFFFU //所有狀態(tài)改變 };
以下是網(wǎng)友整理的運(yùn)行邏輯圖:
通過(guò)上圖我們可以做以下運(yùn)行邏輯的總結(jié):
一個(gè)線程對(duì)應(yīng)一個(gè)runLoop,主線程的runloop是程序一啟動(dòng),默認(rèn)就創(chuàng)建一個(gè)runloop,創(chuàng)建好了之后就會(huì)給它添加一些默認(rèn)的模式,每個(gè)模式里面會(huì)有很多的 source /timer/observer ,添加好這些模式后,observer就會(huì)監(jiān)聽主線程的runloop,進(jìn)入runloop后,就開始處理事件,先處理timer,再處理source0,source0處理完之后再處理source1,當(dāng)把這些所有的事件反復(fù)的處理完之后,如果沒有事件了,那么runloop就會(huì)進(jìn)入睡眠狀態(tài),當(dāng)用戶又觸發(fā)了新的事件,就會(huì)喚醒runloop,喚醒runloop后回到第二步,重新處理新的timer,新的source0,新的source1,處理完后就睡眠,一直反復(fù),當(dāng)我們把程序關(guān)閉或者強(qiáng)退,這個(gè)時(shí)候observer就會(huì)監(jiān)聽都runloop退出了.
observer是監(jiān)聽runloop狀態(tài)的.
timer / source/
CFRunLoopRef創(chuàng)建一個(gè)timer必須添加到runloop 才會(huì)執(zhí)行,添加的時(shí)候要指定模式 defaurce模式 ,不對(duì)程序做任何操作 timer就會(huì)后臺(tái)運(yùn)行 ,當(dāng)我進(jìn)行操作的時(shí)候runloop模式就會(huì)從默認(rèn)模式切換到其他模式,假如說(shuō)我操作scrollerView 它就會(huì)從default模式切換到tracking模式
而roonloop 同一時(shí)刻只能執(zhí)行一種模式.
當(dāng)在創(chuàng)建timer時(shí)指定 Comment 僅僅是個(gè)標(biāo)記,默認(rèn)和追蹤,那么timer就會(huì)隨著觸發(fā)的模式不同進(jìn)行 默認(rèn)和追蹤模式的選擇
Source :source一般是不會(huì)去操作的,第一種是基于自定義的,第二種是基于端口的,第三種是基于Performselect的
通過(guò)函數(shù)調(diào)用棧來(lái)對(duì)source分類:分為source0 source1 :
source0是非基于端口的,是用戶自己手動(dòng)觸發(fā)的操作,比如觸摸滑動(dòng)等操作.
Source1是系統(tǒng)內(nèi)部的一些端口觸發(fā)的事件
子線程的runloop需要手動(dòng)創(chuàng)建,需要手動(dòng)開啟
5.自動(dòng)釋放池,什么時(shí)候創(chuàng)建和釋放 ?
(1)第一次創(chuàng)建:是在runloop進(jìn)入的時(shí)候創(chuàng)建 對(duì)應(yīng)的狀態(tài) = KCFRunLoopEntry
(2)最后一個(gè)退出,是在runloop退出的時(shí)候 對(duì)應(yīng)的狀態(tài) = KCFRunLoopExit
(3)其他的創(chuàng)建和釋放
每次睡覺的時(shí)候會(huì)釋放前自動(dòng)釋放池,再創(chuàng)建一個(gè)新的
即將進(jìn)入睡眠的時(shí)候,先釋放上一次創(chuàng)建的自動(dòng)釋放池,然后再創(chuàng)建一個(gè)新的釋放池