一、Runtime概念
RunTime簡稱運(yùn)行時,其中最主要的是消息機(jī)制。
對于C語言,函數(shù)的調(diào)用在編譯的時候會決定調(diào)用哪個函數(shù),編譯完成之后直接順序執(zhí)行,無任何二義性。
OC的函數(shù)調(diào)用成為消息發(fā)送,屬于動態(tài)調(diào)用過程。在編譯的時候并不能決定真正調(diào)用哪個函數(shù)(事實(shí)證明,在編譯階段,OC可以調(diào)用任何函數(shù),即使這個函數(shù)并未實(shí)現(xiàn),只要聲明過就不會報錯。而C語言在編譯階段就會報錯)。
-
只有在真正運(yùn)行的時候才會根據(jù)函數(shù)的名稱找到對應(yīng)的函數(shù)來調(diào)用。
其動態(tài)性體現(xiàn)在三個方面:
1.動態(tài)類型:
即運(yùn)行時再決定對象的類型。簡單說就是id類型,任何對象都可以被id指針?biāo)?,只有在運(yùn)行時才能決定是什么類型。像內(nèi)置的明確的基本類型都屬于靜態(tài)類型(int、NSString等)。靜態(tài)類型在編譯的時候就能被識別出來。所以,若程序發(fā)生了類型不對應(yīng),編譯器就會發(fā)出警告。而動態(tài)類型就編譯器編譯的時候是不能被識別的,要等到運(yùn)行時(run time),即程序運(yùn)行的時候才會根據(jù)語境來識別。所以這里面就有兩個概念要分清:編譯時跟運(yùn)行時。
2.動態(tài)綁定:
基于動態(tài)類型,在某個實(shí)例對象被確定后,其類型便被確定了。該對象對應(yīng)的屬性和響應(yīng)的消息也被完全確定,這就是動態(tài)綁定。比如我們一般向一個NSObject對象發(fā)送-respondsToSelector:或者 -instancesRespondToSelector:等來確定對象是否可以對某個SEL做出響應(yīng),而在OC消息轉(zhuǎn)發(fā)機(jī)制被觸發(fā)之前,對應(yīng)的類 的+resolveClassMethod:和+resolveInstanceMethod:將會被調(diào)用,在此時有機(jī)會動態(tài)地向類或者實(shí)例添加新的方 法,也即類的實(shí)現(xiàn)是可以動態(tài)綁定的;isKindOfClass也是一樣的道理。
3.動態(tài)加載:
所謂動態(tài)加載就是我們做開發(fā)的時候icon圖片的時候在Retina設(shè)備上要多添加一個張@2x的圖片,當(dāng)設(shè)備更換的時候,圖片也會自動的替換。
二、Runtime數(shù)據(jù)結(jié)構(gòu)
1.Class
Objective-C類是由Class類型來表示,Class 其實(shí)是指向 objc_class 結(jié)構(gòu)體的指針
typedef struct objc_class *Class;
我們可以從<objc/runtime.h>里面看到類的定義
struct object_class{
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息,供運(yùn)行期使用的一些位標(biāo)識
long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
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; // 協(xié)議鏈表
#endif
}OBJC2_UNAVAILABLE;
2.id
id 是一個結(jié)構(gòu)體指針類型,它可以指向 Objective-C 中的任何對象。從<objc/objc.h>中可以看出id其實(shí)是指向objc_object結(jié)構(gòu)體的指針。objc_object只有一個成員變量 isa,對象可以通過 isa 指針找到其所屬的類,這也是為什么id可以表示任意對象的原因。isa 是一個 Class 類型的成員變量。
注意:在KVO中,isa在運(yùn)行時會被修改,指向一個中間類,對于編譯器而言,isa的指向才是最真實(shí)的類型。
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
當(dāng)我們向一個Objective-C對象發(fā)送消息時,運(yùn)行時庫會根據(jù)
實(shí)例對象的isa指針找到這個實(shí)例對象所屬的類。Runtime庫會在類的方法列表由super_class指針找到父類的方法列表直至根類NSObject中去尋找與消息對應(yīng)的selector指向的方法,找到后即運(yùn)行這個方法。
3.元類(meta-Class)
類自身也是一種對象,可以叫做“類對象”。類對象包含一個指向其類的一個isa指針( Class _Nonnull isa )
為了調(diào)用類方法,這個類的isa指針必須指向一個包含這些類方法的一個objc_class結(jié)構(gòu)體。這就引出了meta-class的概念,meta-class中存儲著一個類的所有類方法。
所以,調(diào)用類方法的這個類對象的isa指針指向的就是meta-class。當(dāng)我們向一個對象發(fā)送消息時,runtime會在這個對象所屬的這個類的方法列表中查找方法;而向一個類發(fā)送消息時,會在這個類的meta-class的方法列表中查找。
下圖是一個經(jīng)典的類及相應(yīng)meta-class類的一個繼承體系圖解:

從圖中可以看出:
1.每個實(shí)例對象的類都是類對象,每個類對象的類都是元類對象,每個元類對象的類都是根元類(root meta class的isa指向自身)。即任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類,而基類的meta-class的isa指針是指向它自己。
2.類對象的父類最終繼承自根類對象NSObject,NSObject的父類為nil
3.元類對象(包括根元類)的父類最終繼承自根類對象NSObject
三、方法和消息
1.SEL
typedef struct objc_selector *SEL;
SEL sel1 = @selector(func1);
SEL sel2 = NSSelectorFromString(func2);
SEL本質(zhì)上是一個指向方法的指針。Objective-C在編譯時,會依據(jù)每一個方法的名字、參數(shù)序列,生成一個唯一的整型標(biāo)識即SEL,每一個方法都對應(yīng)著一個SEL。所以即使返回值類型或參數(shù)類型不同,方法名相同也會報錯。
2.IMP
id (*IMP)(id, SEL,...)
IMP的本質(zhì)是函數(shù)指針,指向方法實(shí)現(xiàn)的地址,直接通過IMP就可以找到各個方法。這樣效率更高,因?yàn)槔@過了消息傳遞階段,直接定位。
SEL就是為了查找方法的最終實(shí)現(xiàn)IMP的。由于每個方法對應(yīng)唯一的SEL,因此我們可以通過SEL方便快速準(zhǔn)確地獲得它所對應(yīng)的IMP。
消息發(fā)送和轉(zhuǎn)發(fā)流程可以概括為:消息發(fā)送(Messaging)是 Runtime 通過 selector 快速查找 IMP 的過程,有了函數(shù)指針就可以執(zhí)行對應(yīng)的方法實(shí)現(xiàn);消息轉(zhuǎn)發(fā)(Message Forwarding)是在查找 IMP 失敗后執(zhí)行一系列轉(zhuǎn)發(fā)流程的慢速通道,如果不作轉(zhuǎn)發(fā)處理,則會打日志和拋出異常。
3.Method
typedef struct objc_method *Method
struct objc_method{
SEL method_name OBJC2_UNAVAILABLE; // 方法名
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE; // 方法實(shí)現(xiàn)
}
可以看出method包含SEL和IMP,在實(shí)現(xiàn)方法交換時,主要原理就是交換SEL和IMP的映射關(guān)系。
參考:http://www.itdecent.cn/p/46dd81402f63
http://www.itdecent.cn/p/adf0d566c887
http://www.itdecent.cn/p/13457a27624c