雖然swift 慢慢會越用越多,但憑借oc 強大的runtime機制,oc依舊是現在的中流砥柱.只做記錄探討 加深自己的理解.runTime簡稱運行時。就是系統在運行的時候的一些機制,其中最主要的是消息機制。對于C語言,函數的調用在編譯的時候會決定調用哪個函數( C語言的函數調用請看這里 )。編譯完成之后直接順序執行,無任何二義性。OC的函數調用成為消息發送。屬于動態調用過程。在編譯的時候并不能決定真正調用哪個函數(事實證明,在編 譯階段,OC可以調用任何函數,即使這個函數并未實現,只要申明過就不會報錯。而C語言在編譯階段就會報錯)。只有在真正運行的時候才會根據函數的名稱找 到對應的函數來調用。
那OC是怎么實現動態調用的呢?下面我們來看看OC通過發送消息來達到動態調用的秘密。假如在OC中寫了這樣的一個代碼:
[obj ? ? xMethod]
其中obj是一個對象,xMethod是一個函數名稱。對于這樣一個簡單的調用。在編譯時RunTime會將上述代碼轉化成
objc_msgSend(obj,@selrctor(xMethod));
首先我們先看下obj這個對象,ios中的obj都繼承于NSObject
@interface NSObject <nsobject>{
? ? Class isa ?OBJC_ISA_AVAILABILITY;
}</nsobjct>
在NSObject中存在一個Class的isa指針。然后我們看看Class:
typedef struct objc_class *Class;
struct objc_class {
Class isa? OBJC_ISA_AVAILABILITY;//指向metaclass
#if !__OBJC2__
Class super_class? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//指向父類
const char *name? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//類名
long version? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//類的版本信息,初始化默認為0,可通過runtime函數class_setVersion和class_getVersion進行修改、讀取
long info? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//一些標識信息,如CLS_CLASS(0x1L)表示該類為普通class,其中包含對象方法和成員變量;CLS_META(0x2L)表示該類為metaclass,其中包含類方法;
long instance_size? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//該類的實例變量大小(包含從父類繼承下來的實例變量)
struct objc_ivar_list *ivars? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//用于存儲每個成員變量的地址
struct objc_method_list **methodLists? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//與 info 的一些標志位有關,如CLS_CLASS (0x1L),則存儲對象方法,如CLS_META (0x2L),則存儲類方法;
struct objc_cache *cache? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//指向最近使用的方法的指針,用于提升效率
struct objc_protocol_list *protocols? ? ? ? ? ? ? ? ? ? OBJC2_UNAVAILABLE;
//存儲該類遵守的協議.
#endif
} OBJC2_UNAVAILABLE;
我們可以看到,對于一個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(xMethod):這是一個SEL方法選擇器。SEL其主要是快速的通過方法名字(xMethod)查找到對應方法的函數指針,然后調用其函數。SEL其本身是一個int類型的一個地址,地址中存放著方法的名字。對于一個類中。每一個方法對應著一個SEL。所以iOS類中不能存在2個名稱相同 的方法,即使參數類型不同,因為SEL是根據方法名字生成的,相同的方法名稱只能對應一個SEL。
下面我們就來看看具體消息發送之后是怎么來動態查找對應的方法的。
首先,編譯器將代碼[obj xMethod] 轉化為[objc_msgSend(obj,@selector(xMethod))];在objc_msgSend函數中。首先通過obj的isa指針找到obj對應的class。在class中先去cache中通過SEL查找對應函數method,若cache中未找到。再去methodList中查找,若methodist中未找到,則取superclass中查找。若能找到,則將method加入到cache中,以方便下次查找,并通過method中的函數指針跳轉到對應的函數中去執行。