了解三個(gè)概念Class, SEL, IMP,它們?cè)趏bjc/objc.h 中定義:
typedef struct objc_class *Class;
typedef struct objc_object {
Class isa;
} *id;
typedef struct objc_selector *SEL;
typedef id (*IMP)(id, SEL, ...);
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
Class 是指向類(lèi)結(jié)構(gòu)體的指針,該類(lèi)結(jié)構(gòu)體含有一個(gè)指向其父類(lèi)類(lèi)結(jié)構(gòu)的指針,該類(lèi)方法的鏈表,該類(lèi)方法的緩存以及其他必要信息。
NSObject 的class 方法就返回這樣一個(gè)指向其類(lèi)結(jié)構(gòu)的指針。每一個(gè)類(lèi)實(shí)例對(duì)象的第一個(gè)實(shí)例變量是一個(gè)指向該對(duì)象的類(lèi)結(jié)構(gòu)的指針,叫做isa。通過(guò)該指針,對(duì)象可以訪問(wèn)它對(duì)應(yīng)的類(lèi)以及相應(yīng)的父類(lèi)。如圖一所示:
如圖一所示,圓形所代表的實(shí)例對(duì)象的第一個(gè)實(shí)例變量為 isa,它指向該類(lèi)的類(lèi)結(jié)構(gòu) The object’s class。而該類(lèi)結(jié)構(gòu)有一個(gè)指向其父類(lèi)類(lèi)結(jié)構(gòu)的指針superclass, 以及自身消息名稱(selector)/實(shí)現(xiàn)地址(address)的方法鏈表。
方法的含義:
注意這里所說(shuō)的方法鏈表里面存儲(chǔ)的是Method 類(lèi)型的。圖一中selector 就是指 Method的 SEL, address就是指Method的 IMP。 Method 在頭文件 objc_class.h中定義如下:
typedef struct objc_method *Method;
typedef struct objc_ method {
SEL method_name;
char *method_types;
IMP method_imp;
};
方法 Method,其包含
一個(gè) SEL – 表示該方法的名稱,
一個(gè)types – 表示該方法參數(shù)的類(lèi)型,
一個(gè) IMP - 指向該方法的具體實(shí)現(xiàn)的函數(shù)指針。
不同的類(lèi)可以擁有相同的 selector,這個(gè)沒(méi)有問(wèn)題,因?yàn)椴煌?lèi)的實(shí)例對(duì)象performSelector相同的 selector 時(shí),會(huì)在各自的消息選標(biāo)(selector)/實(shí)現(xiàn)地址(address) 方法鏈表中根據(jù) selector 去查找具體的方法實(shí)現(xiàn)IMP, 然后用這個(gè)方法實(shí)現(xiàn)去執(zhí)行具體的實(shí)現(xiàn)代碼。這是一個(gè)動(dòng)態(tài)綁定的過(guò)程,在編譯的時(shí)候,我們不知道最終會(huì)執(zhí)行哪一些代碼,只有在執(zhí)行的時(shí)候,通過(guò)selector去查詢,我們才能確定具體的執(zhí)行代碼。
IMP 的含義:
在前面我們也看到 IMP 的定義為:
typedef id (*IMP)(id, SEL, ...);
至此,我們就很清楚地知道 IMP 的含義:IMP 是一個(gè)函數(shù)指針,這個(gè)被指向的函數(shù)包含一個(gè)接收消息的對(duì)象id(self 指針), 調(diào)用方法的選標(biāo) SEL (方法名),以及不定個(gè)數(shù)的方法參數(shù),并返回一個(gè)id。也就是說(shuō) IMP 是消息最終調(diào)用的執(zhí)行代碼,是方法真正的實(shí)現(xiàn)代碼 。我們可以像在C語(yǔ)言里面一樣使用這個(gè)函數(shù)指針。
消息傳遞(Messaging)
在 Objective-C 中,[object foo]語(yǔ)法并不會(huì)立即執(zhí)行 foo 這個(gè)方法的代碼。它是在運(yùn)行時(shí)給 object 發(fā)送一條叫 foo 的消息。這個(gè)消息,也許會(huì)由 object 來(lái)處理,也許會(huì)被轉(zhuǎn)發(fā)給另一個(gè)對(duì)象,或者不予理睬假裝沒(méi)收到這個(gè)消息。多條不同的消息也可以對(duì)應(yīng)同一個(gè)方法實(shí)現(xiàn)。這些都是在程序運(yùn)行的時(shí)候決定的。
從這些定義中可以看出發(fā)送一條消息也就 objc_msgSend做了什么事。舉 objc_msgSend(obj, foo)
這個(gè)例子來(lái)說(shuō):
1.首先,通過(guò) obj 的 isa 指針找到它的 class ;
2.在 class 的 method list 找 foo ;
3.如果 class 中沒(méi)到 foo,繼續(xù)往它的 superclass 中找 ;
4.一旦找到 foo 這個(gè)函數(shù),就去執(zhí)行它的實(shí)現(xiàn)IMP .
但這種實(shí)現(xiàn)有個(gè)問(wèn)題,效率低。但一個(gè) class 往往只有 20% 的函數(shù)會(huì)被經(jīng)常調(diào)用,可能占總調(diào)用次數(shù)的 80% 。每個(gè)消息都需要遍歷一次 objc_method_list并不合理。如果把經(jīng)常被調(diào)用的函數(shù)緩存下來(lái),那可以大大提高函數(shù)查詢的效率。這也就是 objc_class中另一個(gè)重要成員 objc_cache做的事情 - 再找到 foo 之后,把 foo 的 method_name作為 key ,method_imp作為 value 給存起來(lái)。當(dāng)再次收到 foo 消息的時(shí)候,可以直接在 cache 里找到,避免去遍歷 objc_method_list.
首先,編譯器將代碼[obj makeText];轉(zhuǎn)化為objc_msgSend(obj, @selector (makeText));,在objc_msgSend函數(shù)中。首先通過(guò)obj的isa指針找到obj對(duì)應(yīng)的class。在Class中先去cache中 通過(guò)SEL查找對(duì)應(yīng)函數(shù)method(猜測(cè)cache中method列表是以SEL為key通過(guò)hash表來(lái)存儲(chǔ)的,這樣能提高函數(shù)查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,則取superClass中查找。若能找到,則將method加 入到cache中,以方便下次查找,并通過(guò)method中的函數(shù)指針跳轉(zhuǎn)到對(duì)應(yīng)的函數(shù)中去執(zhí)行。
參考:
http://blog.csdn.net/kesalin/article/details/6689226
http://southpeak.github.io/blog/2014/10/25/objective-c-runtime-yun-xing-shi-zhi-lei-yu-dui-xiang/