Runloop(運(yùn)行循環(huán))
保持程序的持續(xù)運(yùn)行--內(nèi)部就是do-while循環(huán),在這個(gè)循環(huán)內(nèi)部不斷地處理各種任務(wù)
(比如Source、Timer、Observer)
處理app中的各種事件(比如觸摸事件、定時(shí)器事件【NSTimer】、selector事件
【選擇器·performSelector···】)
節(jié)省CPU資源,提高程序性能,有事情就做事情,沒事情就休息
Runloop是程序一直存在并不斷處理事件的原因
main函數(shù)中的Runloop
a. 在UIApplication函數(shù)內(nèi)部就啟動了一個(gè)Runloop 該函數(shù)返回一個(gè)int類型的值
b. 默認(rèn)啟動的Runloop是跟主線程相關(guān)聯(lián)的
在iOS開發(fā)中有兩套api來訪問Runloop
a. foundation框架【NSRunloop】
b. core foundation框架【CFRunloopRef】
c. NSRunLoop和CFRunLoopRef都代表著RunLoop對象,它們是等價(jià)的,可以互相轉(zhuǎn)換
d. NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內(nèi)部結(jié)構(gòu),需要多研究
CFRunLoopRef層面的API(Core Foundation層面)
Runloop和線程的關(guān)系:一個(gè)Runloop對應(yīng)著一條唯一的線程,一條線程最多有一個(gè)Runloop;開啟一個(gè)Runloop能讓子線程不死
Runloop的創(chuàng)建:主線程Runloop已經(jīng)創(chuàng)建好了,子線程的runloop需要手動創(chuàng)建并開啟
Runloop的生命周期:在第一次獲取時(shí)創(chuàng)建,在線程結(jié)束時(shí)銷毀
1.獲得當(dāng)前Runloop對象
//01 NSRunloop
NSRunLoop*runloop1=[NSRunLoopcurrentRunLoop];
//02 CFRunLoopRef
CFRunLoopRefrunloop2=CFRunLoopGetCurrent();
?
2.拿到當(dāng)前應(yīng)用程序的主Runloop(主線程對應(yīng)的Runloop)
//01 NSRunloop
NSRunLoop*runloop1=[NSRunLoopmainRunLoop];
//02 CFRunLoopRef
CFRunLoopRefrunloop2=CFRunLoopGetMain();
?
/*3.注意點(diǎn):開一個(gè)子線程創(chuàng)建runloop,不是通過alloc init方法創(chuàng)建,而是直接通過調(diào)用
currentRunLoop方法來創(chuàng)建,它本身是一個(gè)懶加載的。如果不存在那么會自動創(chuàng)建一個(gè)該線程
對應(yīng)的runloop對象返回
4.在子線程中,如果不主動獲取Runloop的話,那么子線程內(nèi)部是不會創(chuàng)建Runloop的。可以下載
CFRunloopRef的源碼,搜索_CFRunloopGet0,查看代碼。
5.Runloop對象是利用字典來進(jìn)行存儲,而且key是對應(yīng)的線程Value為該線程對應(yīng)的Runloop。
*/
/*
1)開啟NSTimer,控制定時(shí)器在特定模式下執(zhí)行
2)可以讓某些事件(行為、任務(wù))在特定模式下執(zhí)行,如ImageView顯示:控制方法在特定的模式下可用
3)PerformSelector:控制線程執(zhí)行不同的任務(wù)
4)常駐線程:讓一個(gè)子線程不進(jìn)入消亡狀態(tài),等待其他線程發(fā)來消息,處理其他事件
5)可以添加Observer監(jiān)聽RunLoop的狀態(tài),比如監(jiān)聽點(diǎn)擊事件的處理(在所有點(diǎn)擊事件之前做一些事情
6)自動釋放池(可通過Observer監(jiān)聽RunLoop的狀態(tài))
第一次創(chuàng)建:進(jìn)入runloop的時(shí)候
最后一次釋放:runloop退出的時(shí)候
其它創(chuàng)建和釋放:當(dāng)runloop即將休眠的時(shí)候會把之前的自動釋放池釋放,然后重新創(chuàng)建一個(gè)新的釋放池
?
常駐線程實(shí)現(xiàn):
主線程對應(yīng)的runloop默認(rèn)已經(jīng)創(chuàng)建好了,但是子線程對應(yīng)的runloop需要手動創(chuàng)建
子線程對應(yīng)的runloop還需要手動的開啟,否則該runloop一創(chuàng)建出來就退出;在開啟前要先添加事件,否則也會退出。
保證mode里面有事件,不讓runloop立刻退出
可以往runloop中添加source和timer,但是添加observer是沒有作用的
*/
/*
NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timer) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
*/
?
//創(chuàng)建子線程對應(yīng)的runloop并添加基于port的source
[[NSRunLoopcurrentRunLoop]addPort:[NSMachPortport]forMode:NSDefaultRunLoopMode];
?
//開啟runloop,運(yùn)行在默認(rèn)模式下面(添加timer或是port都要開始runloop)
[[NSRunLoopcurrentRunLoop]run];
請簡單說明runloop中幾個(gè)類之間的相互關(guān)系(runloop & source & timer &observer &mode)
a.CFRunloopRef
b.CFRunloopModeRef【Runloop的運(yùn)行模式】
c.CFRunloopSourceRef【Runloop要處理的事件源】
d.CFRunloopTimerRef【Timer事件】
e.CFRunloopObserverRef【Runloop的觀察者(監(jiān)聽者)】
runloop啟動之后會選擇一種運(yùn)行模式,在執(zhí)行執(zhí)行會先檢查運(yùn)行模式內(nèi)部是否有source和timers,如果一個(gè)sourc或者是一個(gè)timer都沒有那么runlooop啟動之后就立刻退出了。
runlooop的source有兩種分類方法
按照以前的分類方法可以分為: 基于端口的? 自定義的? performSelector事件
按照函數(shù)調(diào)用棧來劃分:? source0? soucrce1
observer,可以用來監(jiān)聽當(dāng)前runloop運(yùn)行狀態(tài)的改變,注意(Core foundation框架)
NSTimer必須添加到runloop中才會工作,且其工作收到runloop運(yùn)行模式的影響。defultMode? UItrackingMode
什么時(shí)候使用run loop
僅當(dāng)在為你的程序創(chuàng)建輔助線程的時(shí)候,你才需要顯式運(yùn)行一個(gè)run loop。Run loop是程序主線程基礎(chǔ)設(shè)施的關(guān)鍵部分。所以,Cocoa和Carbon程序提供了代碼運(yùn)行主程序的循環(huán)并自動啟動run loop。IOS程序中UIApplication的run方法(或Mac OS X中的NSApplication)作為程序啟動步驟的一部分,它在程序正常啟動的時(shí)候就會啟動程序的主循環(huán)。類似的,RunApplicationEventLoop函數(shù)為Carbon程序啟動主循環(huán)。如果你使用xcode提供的模板創(chuàng)建你的程序,那你永遠(yuǎn)不需要自己去顯式的調(diào)用這些例程。
對于輔助線程,你需要判斷一個(gè)run loop是否是必須的。如果是必須的,那么你要自己配置并啟動它。你不需要在任何情況下都去啟動一個(gè)線程的run loop。比如,你使用線程來處理一個(gè)預(yù)先定義的長時(shí)間運(yùn)行的任務(wù)時(shí),你應(yīng)該避免啟動run loop。Run loop在你要和線程有更多的交互時(shí)才需要,比如以下情況:
使用端口或自定義輸入源來和其他線程通信
使用線程的定時(shí)器
Cocoa中使用任何performSelector…的方法
使線程周期性工作