一.RunLoop機(jī)制:
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)事件龟梦。
(1).RunLoop的主要目的:
保證程序執(zhí)行的線程不會(huì)被系統(tǒng)終止隐锭。
(2).什么時(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)RunLoop的循環(huán),也就是說,需要代碼提供while或者for循環(huán)來驅(qū)動(dòng)RunLoop.
在這個(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)有時(shí)間發(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中.
二.Runtime機(jī)制:
RunTime簡(jiǎn)稱運(yùn)行時(shí)拉一。就是系統(tǒng)在運(yùn)行的時(shí)候的一些機(jī)制采盒,其中最主要的是消息機(jī)制。對(duì)于C語言蔚润,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)(
C語言的函數(shù)調(diào)用請(qǐng)看這里
)磅氨。編譯完成之后直接順序執(zhí)行,無任何二義性嫡纠。OC的函數(shù)調(diào)用成為消息發(fā)送烦租。屬于動(dòng)態(tài)調(diào)用過程。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)(事實(shí)證明除盏,在編
譯階段叉橱,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn)痴颊,只要申明過就不會(huì)報(bào)錯(cuò)赏迟。而C語言在編譯階段就會(huì)報(bào)錯(cuò))。只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找
到對(duì)應(yīng)的函數(shù)來調(diào)用蠢棱。
那OC是怎么實(shí)現(xiàn)動(dòng)態(tài)調(diào)用的呢锌杀?下面我們來看看OC通過發(fā)送消息來達(dá)到動(dòng)態(tài)調(diào)用的秘密甩栈。假如在OC中寫了這樣的一個(gè)代碼:
[objc] view plain?
[obj?makeText];
其中obj是一個(gè)對(duì)象,makeText是一個(gè)函數(shù)名稱糕再。對(duì)于這樣一個(gè)簡(jiǎn)單的調(diào)用量没。在編譯時(shí)RunTime會(huì)將上述代碼轉(zhuǎn)化成
[objc] view plain?
objc_msgSend(obj,@selector(makeText));
首先我們來看看obj這個(gè)對(duì)象,iOS中的obj都繼承于NSObject突想。
[objc] view plain?
@interface?NSObject??{
Class?isa??OBJC_ISA_AVAILABILITY;
}
在NSObjcet中存在一個(gè)Class的isa指針殴蹄。然后我們看看Class:
[objc] view plain?
typedef?struct?objc_class?*Class;
struct?objc_class?{
Class?isa;?//?指向metaclass
Class?super_class?;?//?指向其父類
const?charchar?*name?;?//?類名
long?version?;?//?類的版本信息,初始化默認(rèn)為0猾担,可以通過runtime函數(shù)class_setVersion和class_getVersion進(jìn)行修改袭灯、讀取
long?info;?//?一些標(biāo)識(shí)信息,如CLS_CLASS?(0x1L)?表示該類為普通?class?,其中包含對(duì)象方法和成員變量;CLS_META?(0x2L)?表示該類為?metaclass绑嘹,其中包含類方法;
long?instance_size?;?//?該類的實(shí)例變量大小(包括從父類繼承下來的實(shí)例變量);
struct?objc_ivar_list?*ivars;?//?用于存儲(chǔ)每個(gè)成員變量的地址
struct?objc_method_list?**methodLists?;?//?與?info?的一些標(biāo)志位有關(guān),如CLS_CLASS?(0x1L),則存儲(chǔ)對(duì)象方法稽荧,如CLS_META?(0x2L),則存儲(chǔ)類方法;
struct?objc_cache?*cache;?//?指向最近使用的方法的指針工腋,用于提升效率姨丈;
struct?objc_protocol_list?*protocols;?//?存儲(chǔ)該類遵守的協(xié)議
}
我們可以看到,對(duì)于一個(gè)Class類中擅腰,存在很多東西蟋恬,下面我來一一解釋一下:
Class
isa:指向metaclass,也就是靜態(tài)的Class趁冈。一般一個(gè)Obj對(duì)象中的isa會(huì)指向普通的Class歼争,這個(gè)Class中存儲(chǔ)普通成員變量和對(duì)
象方法(“-”開頭的方法),普通Class中的isa指針指向靜態(tài)Class箱歧,靜態(tài)Class中存儲(chǔ)static類型成員變量和類方法(“+”開頭的方
法)矾飞。
Class super_class:指向父類,如果這個(gè)類是根類呀邢,則為NULL洒沦。
注意:所有metaclass中isa指針都指向跟metaclass。而跟metaclass則指向自身价淌。
Root metaclass是通過繼承Root class產(chǎn)生的申眼。與root class結(jié)構(gòu)體成員一致,也就是前面提到的結(jié)構(gòu)蝉衣。不同的是Root
metaclass的isa指針指向自身括尸。
Class類中其他的成員這里就先不做過多解釋了,下面我們來看看:
@selector (makeText):
這是一個(gè)SEL方法選擇器病毡。SEL其主要作用是快速的通過方法名字(makeText)查找到對(duì)應(yīng)方法的函數(shù)指針濒翻,然后調(diào)用其函數(shù)。SEL其本身是一個(gè)
Int類型的一個(gè)地址,地址中存放著方法的名字有送。對(duì)于一個(gè)類中淌喻。每一個(gè)方法對(duì)應(yīng)著一個(gè)SEL。所以iOS類中不能存在2個(gè)名稱相同的方法雀摘,即使參數(shù)類型不
同裸删,因?yàn)镾EL是根據(jù)方法名字生成的,相同的方法名稱只能對(duì)應(yīng)一個(gè)SEL阵赠。
下面我們就來看看具體消息發(fā)送之后是怎么來動(dòng)態(tài)查找對(duì)應(yīng)的方法的涯塔。
首先,編譯器將代碼[obj makeText];轉(zhuǎn)化為objc_msgSend(obj, @selector
(makeText));清蚀,在objc_msgSend函數(shù)中匕荸。首先通過obj的isa指針找到obj對(duì)應(yīng)的class。在Class中先去cache中
通過SEL查找對(duì)應(yīng)函數(shù)method(猜測(cè)cache中method列表是以SEL為key通過hash表來存儲(chǔ)的枷邪,這樣能提高函數(shù)查找速度)每聪,若
cache中未找到。再去methodList中查找齿风,若methodlist中未找到,則取superClass中查找绑洛。若能找到救斑,則將method加
入到cache中,以方便下次查找真屯,并通過method中的函數(shù)指針跳轉(zhuǎn)到對(duì)應(yīng)的函數(shù)中去執(zhí)行脸候。