一.RunLoop:
Runloop是事件接收和分發機制的一個實現。
Runloop提供了一種異步執行代碼的機制,不能並行執行任務。
在主隊列中,Main RunLoop直接配合任務的執行,負責處理UI事件、定時器以及其他內核相關事件。
(1).RunLoop的主要目的:
保證程序執行的線程不會被系統終止。
(2).什麼時候使用Runloop ?
當需要和該線程進行交互的時候纔會使用Runloop.
每一個線程都有其對應的RunLoop,但是默認非主線程的RunLoop是沒有運行的,需要爲RunLoop添加至少一個事件源,然後去run它。
一般情況下我們是沒有必要去啓用線程的RunLoop的,除非你在一個單獨的線程中需要長久的檢測某個事件。
主線程默認有Runloop。當自己啓動一個線程,如果只是用於處理單一的事件,則該線程在執行完之後就退出了。所以當我們需要讓該線程監聽某項事務時,就得讓線程一直不退出,runloop就是這麼一個循環,沒有事件的時候,一直卡着,有事件來臨了,執行其對應的函數。
RunLoop,正如其名所示,是線程進入和被線程用來相應事件以及調用事件處理函數的地方.需要在代碼中使用控制語句實現RunLoop的循環,也就是說,需要代碼提供while或者for循環來驅動RunLoop.
在這個循環中,使用一個runLoop對象[NSRunloop currentRunloop]執行接收消息,調用對應的處理函數.
Runloop接收兩種源事件:input sources和timer sources。
input sources 傳遞異步事件,通常是來自其他線程和不同的程序中的消息;
timer sources(定時器) 傳遞同步事件(重複執行或者在特定時間上觸發)。
除了處理input sources,Runloop 也會產生一些關於本身行爲的notificaiton。註冊成爲Runloop的observer,可以接收到這些notification,做一些額外的處理。(使用CoreFoundation來成爲runloop的observer)。
Runloop工作的特點:
1>當有時間發生時,Runloop會根據具體的事件類型通知應用程序作出相應;
2>當沒有事件發生時,Runloop會進入休眠狀態,從而達到省電的目的;
3>當事件再次發生時,Runloop會被重新喚醒,處理事件.
提示:一般在開發中很少會主動創建Runloop,而通常會把事件添加到Runloop中.
二.Runtime:
RunTime簡稱運行時。就是系統在運行的時候的一些機制,其中最主要的是消息機制。對於C語言,函數的調用在編譯的時候會決定調用哪個函數( C語言的函數調用請看這裏 )。編譯完成之後直接順序執行,無任何二義性。OC的函數調用成爲消息發送。屬於動態調用過程。在編譯的時候並不能決定真正調用哪個函數(事實證明,在編譯階段,OC可以調用任何函數,即使這個函數並未實現,只要申明過就不會報錯。而C語言在編譯階段就會報錯)。只有在真正運行的時候纔會根據函數的名稱找到對應的函數來調用。
那OC是怎麼實現動態調用的呢?下面我們來看看OC通過發送消息來達到動態調用的祕密。假如在OC中寫了這樣的一個代碼:
<span style="font-size:18px;">[obj makeText];</span>
其中obj是一個對象,makeText是一個函數名稱。對於這樣一個簡單的調用。在編譯時RunTime會將上述代碼轉化成objc_msgSend(obj,@selector(makeText));
首先我們來看看obj這個對象,iOS中的obj都繼承於NSObject。
@interface NSObject <nsobject> {
Class isa OBJC_ISA_AVAILABILITY;
}</nsobject>
在NSObjcet中存在一個Class的isa指針。然後我們看看Class:typedef struct objc_class *Class;
struct objc_class {
Class isa; // 指向metaclass
Class super_class ; // 指向其父類
const char *name ; // 類名
long version ; // 類的版本信息,初始化默認爲0,可以通過runtime函數class_setVersion和class_getVersion進行修改、讀取
long info; // 一些標識信息,如CLS_CLASS (0x1L) 表示該類爲普通 class ,其中包含對象方法和成員變量;CLS_META (0x2L) 表示該類爲 metaclass,其中包含類方法;
long instance_size ; // 該類的實例變量大小(包括從父類繼承下來的實例變量);
struct objc_ivar_list *ivars; // 用於存儲每個成員變量的地址
struct objc_method_list **methodLists ; // 與 info 的一些標誌位有關,如CLS_CLASS (0x1L),則存儲對象方法,如CLS_META (0x2L),則存儲類方法;
struct objc_cache *cache; // 指向最近使用的方法的指針,用於提升效率;
struct objc_protocol_list *protocols; // 存儲該類遵守的協議
}
我們可以看到,對於一個Class類中,存在很多東西,下面我來一一解釋一下:
Class isa:指向metaclass,也就是靜態的Class。一般一個Obj對象中的isa會指向普通的Class,這個Class中存儲普通成員變量和對象方法(“-”開頭的方法),普通Class中的isa指針指向靜態Class,靜態Class中存儲static類型成員變量和類方法(“+”開頭的方法)。
Class super_class:指向父類,如果這個類是根類,則爲NULL。
下面一張圖片很好的描述了類和對象的繼承關係:
注意:所有metaclass中isa指針都指向跟metaclass。而跟metaclass則指向自身。Root metaclass是通過繼承Root class產生的。與root class結構體成員一致,也就是前面提到的結構。不同的是Root metaclass的isa指針指向自身。
Class類中其他的成員這裏就先不做過多解釋了,下面我們來看看:
@selector (makeText):
這是一個SEL方法選擇器。SEL其主要作用是快速的通過方法名字(makeText)查找到對應方法的函數指針,然後調用其函數。SEL其本身是一個Int類型的一個地址,地址中存放着方法的名字。對於一個類中。每一個方法對應着一個SEL。所以iOS類中不能存在2個名稱相同的方法,即使參數類型不同,因爲SEL是根據方法名字生成的,相同的方法名稱只能對應一個SEL。
下面我們就來看看具體消息發送之後是怎麼來動態查找對應的方法的。
首先,編譯器將代碼[obj makeText];轉化爲objc_msgSend(obj, @selector (makeText));,在objc_msgSend函數中。首先通過obj的isa指針找到obj對應的class。在Class中先去cache中通過SEL查找對應函數method(猜測cache中method列表是以SEL爲key通過hash表來存儲的,這樣能提高函數查找速度),若cache中未找到。再去methodList中查找,若methodlist中未找到,則取superClass中查找。若能找到,則將method加入到cache中,以方便下次查找,並通過method中的函數指針跳轉到對應的函數中去執行。