了解三個概念Class, SEL, IMP跟畅,它們在objc/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 是指向類結(jié)構(gòu)體的指針咽筋,該類結(jié)構(gòu)體含有一個指向其父類類結(jié)構(gòu)的指針,該類方法的鏈表徊件,該類方法的緩存以及其他必要信息奸攻。
NSObject 的class 方法就返回這樣一個指向其類結(jié)構(gòu)的指針。每一個類實例對象的第一個實例變量是一個指向該對象的類結(jié)構(gòu)的指針虱痕,叫做isa睹耐。通過該指針,對象可以訪問它對應(yīng)的類以及相應(yīng)的父類部翘。如圖一所示:
如圖一所示硝训,圓形所代表的實例對象的第一個實例變量為 isa,它指向該類的類結(jié)構(gòu) The object’s class新思。而該類結(jié)構(gòu)有一個指向其父類類結(jié)構(gòu)的指針superclass窖梁, 以及自身消息名稱(selector)/實現(xiàn)地址(address)的方法鏈表。
方法的含義:
注意這里所說的方法鏈表里面存儲的是Method 類型的夹囚。圖一中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,其包含
一個 SEL – 表示該方法的名稱荸哟,
一個types – 表示該方法參數(shù)的類型假哎,
一個 IMP - 指向該方法的具體實現(xiàn)的函數(shù)指針。
不同的類可以擁有相同的 selector鞍历,這個沒有問題舵抹,因為不同類的實例對象performSelector相同的 selector 時,會在各自的消息選標(biāo)(selector)/實現(xiàn)地址(address) 方法鏈表中根據(jù) selector 去查找具體的方法實現(xiàn)IMP, 然后用這個方法實現(xiàn)去執(zhí)行具體的實現(xiàn)代碼堰燎。這是一個動態(tài)綁定的過程掏父,在編譯的時候,我們不知道最終會執(zhí)行哪一些代碼秆剪,只有在執(zhí)行的時候赊淑,通過selector去查詢,我們才能確定具體的執(zhí)行代碼仅讽。
IMP 的含義:
在前面我們也看到 IMP 的定義為:
typedef id (*IMP)(id, SEL, ...);
至此陶缺,我們就很清楚地知道 IMP 的含義:IMP 是一個函數(shù)指針,這個被指向的函數(shù)包含一個接收消息的對象id(self 指針), 調(diào)用方法的選標(biāo) SEL (方法名)洁灵,以及不定個數(shù)的方法參數(shù)饱岸,并返回一個id掺出。也就是說 IMP 是消息最終調(diào)用的執(zhí)行代碼,是方法真正的實現(xiàn)代碼 苫费。我們可以像在C語言里面一樣使用這個函數(shù)指針汤锨。
消息傳遞(Messaging)
在 Objective-C 中,[object foo]語法并不會立即執(zhí)行 foo 這個方法的代碼百框。它是在運行時給 object 發(fā)送一條叫 foo 的消息闲礼。這個消息,也許會由 object 來處理铐维,也許會被轉(zhuǎn)發(fā)給另一個對象柬泽,或者不予理睬假裝沒收到這個消息。多條不同的消息也可以對應(yīng)同一個方法實現(xiàn)嫁蛇。這些都是在程序運行的時候決定的锨并。
從這些定義中可以看出發(fā)送一條消息也就 objc_msgSend做了什么事。舉 objc_msgSend(obj, foo)
這個例子來說:
1.首先睬棚,通過 obj 的 isa 指針找到它的 class ;
2.在 class 的 method list 找 foo ;
3.如果 class 中沒到 foo第煮,繼續(xù)往它的 superclass 中找 ;
4.一旦找到 foo 這個函數(shù),就去執(zhí)行它的實現(xiàn)IMP .
但這種實現(xiàn)有個問題闸拿,效率低空盼。但一個 class 往往只有 20% 的函數(shù)會被經(jīng)常調(diào)用,可能占總調(diào)用次數(shù)的 80% 新荤。每個消息都需要遍歷一次 objc_method_list并不合理揽趾。如果把經(jīng)常被調(diào)用的函數(shù)緩存下來,那可以大大提高函數(shù)查詢的效率苛骨。這也就是 objc_class中另一個重要成員 objc_cache做的事情 - 再找到 foo 之后篱瞎,把 foo 的 method_name作為 key ,method_imp作為 value 給存起來痒芝。當(dāng)再次收到 foo 消息的時候俐筋,可以直接在 cache 里找到,避免去遍歷 objc_method_list.
首先严衬,編譯器將代碼[obj makeText];轉(zhuǎn)化為objc_msgSend(obj, @selector (makeText));澄者,在objc_msgSend函數(shù)中。首先通過obj的isa指針找到obj對應(yīng)的class请琳。在Class中先去cache中 通過SEL查找對應(yīng)函數(shù)method(猜測cache中method列表是以SEL為key通過hash表來存儲的粱挡,這樣能提高函數(shù)查找速度),若 cache中未找到俄精。再去methodList中查找询筏,若methodlist中未找到,則取superClass中查找竖慧。若能找到嫌套,則將method加 入到cache中逆屡,以方便下次查找,并通過method中的函數(shù)指針跳轉(zhuǎn)到對應(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/