一、Runtime概念
RunTime簡稱運(yùn)行時(shí),其中最主要的是消息機(jī)制。
對于C語言竭贩,函數(shù)的調(diào)用在編譯的時(shí)候會決定調(diào)用哪個(gè)函數(shù),編譯完成之后直接順序執(zhí)行莺禁,無任何二義性留量。
OC的函數(shù)調(diào)用成為消息發(fā)送,屬于動態(tài)調(diào)用過程。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)(事實(shí)證明楼熄,在編譯階段忆绰,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn)可岂,只要聲明過就不會報(bào)錯(cuò)错敢。而C語言在編譯階段就會報(bào)錯(cuò))。
-
只有在真正運(yùn)行的時(shí)候才會根據(jù)函數(shù)的名稱找到對應(yīng)的函數(shù)來調(diào)用缕粹。
其動態(tài)性體現(xiàn)在三個(gè)方面:
1.動態(tài)類型:
即運(yùn)行時(shí)再決定對象的類型稚茅。簡單說就是id類型,任何對象都可以被id指針?biāo)钙秸叮挥性谶\(yùn)行時(shí)才能決定是什么類型亚享。像內(nèi)置的明確的基本類型都屬于靜態(tài)類型(int、NSString等)绘面。靜態(tài)類型在編譯的時(shí)候就能被識別出來欺税。所以,若程序發(fā)生了類型不對應(yīng)飒货,編譯器就會發(fā)出警告魄衅。而動態(tài)類型就編譯器編譯的時(shí)候是不能被識別的,要等到運(yùn)行時(shí)(run time)塘辅,即程序運(yùn)行的時(shí)候才會根據(jù)語境來識別晃虫。所以這里面就有兩個(gè)概念要分清:編譯時(shí)跟運(yùn)行時(shí)。
2.動態(tài)綁定:
基于動態(tài)類型扣墩,在某個(gè)實(shí)例對象被確定后哲银,其類型便被確定了。該對象對應(yīng)的屬性和響應(yīng)的消息也被完全確定呻惕,這就是動態(tài)綁定荆责。比如我們一般向一個(gè)NSObject對象發(fā)送-respondsToSelector:或者 -instancesRespondToSelector:等來確定對象是否可以對某個(gè)SEL做出響應(yīng),而在OC消息轉(zhuǎn)發(fā)機(jī)制被觸發(fā)之前亚脆,對應(yīng)的類 的+resolveClassMethod:和+resolveInstanceMethod:將會被調(diào)用做院,在此時(shí)有機(jī)會動態(tài)地向類或者實(shí)例添加新的方 法,也即類的實(shí)現(xiàn)是可以動態(tài)綁定的濒持;isKindOfClass也是一樣的道理键耕。
3.動態(tài)加載:
所謂動態(tài)加載就是我們做開發(fā)的時(shí)候icon圖片的時(shí)候在Retina設(shè)備上要多添加一個(gè)張@2x的圖片,當(dāng)設(shè)備更換的時(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 是一個(gè)結(jié)構(gòu)體指針類型官套,它可以指向 Objective-C 中的任何對象酒奶。從<objc/objc.h>中可以看出id其實(shí)是指向objc_object結(jié)構(gòu)體的指針蚁孔。objc_object只有一個(gè)成員變量 isa,對象可以通過 isa 指針找到其所屬的類惋嚎,這也是為什么id可以表示任意對象的原因杠氢。isa 是一個(gè) Class 類型的成員變量。
注意:在KVO中另伍,isa在運(yùn)行時(shí)會被修改修然,指向一個(gè)中間類,對于編譯器而言质况,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)我們向一個(gè)Objective-C對象發(fā)送消息時(shí),運(yùn)行時(shí)庫會根據(jù)
實(shí)例對象的isa指針找到這個(gè)實(shí)例對象所屬的類结榄。Runtime庫會在類的方法列表由super_class指針找到父類的方法列表直至根類NSObject中去尋找與消息對應(yīng)的selector指向的方法中贝,找到后即運(yùn)行這個(gè)方法。
3.元類(meta-Class)
類自身也是一種對象臼朗,可以叫做“類對象”邻寿。類對象包含一個(gè)指向其類的一個(gè)isa指針( Class _Nonnull isa )
為了調(diào)用類方法,這個(gè)類的isa指針必須指向一個(gè)包含這些類方法的一個(gè)objc_class結(jié)構(gòu)體视哑。這就引出了meta-class的概念绣否,meta-class中存儲著一個(gè)類的所有類方法。
所以挡毅,調(diào)用類方法的這個(gè)類對象的isa指針指向的就是meta-class蒜撮。當(dāng)我們向一個(gè)對象發(fā)送消息時(shí),runtime會在這個(gè)對象所屬的這個(gè)類的方法列表中查找方法跪呈;而向一個(gè)類發(fā)送消息時(shí)段磨,會在這個(gè)類的meta-class的方法列表中查找。
下圖是一個(gè)經(jīng)典的類及相應(yīng)meta-class類的一個(gè)繼承體系圖解:
從圖中可以看出:
1.每個(gè)實(shí)例對象的類都是類對象耗绿,每個(gè)類對象的類都是元類對象苹支,每個(gè)元類對象的類都是根元類(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ì)上是一個(gè)指向方法的指針。Objective-C在編譯時(shí)奴紧,會依據(jù)每一個(gè)方法的名字特姐、參數(shù)序列晶丘,生成一個(gè)唯一的整型標(biāo)識即SEL黍氮,每一個(gè)方法都對應(yīng)著一個(gè)SEL唐含。所以即使返回值類型或參數(shù)類型不同,方法名相同也會報(bào)錯(cuò)沫浆。
2.IMP
id (*IMP)(id, SEL,...)
IMP的本質(zhì)是函數(shù)指針捷枯,指向方法實(shí)現(xiàn)的地址,直接通過IMP就可以找到各個(gè)方法专执。這樣效率更高淮捆,因?yàn)槔@過了消息傳遞階段,直接定位本股。
SEL就是為了查找方法的最終實(shí)現(xiàn)IMP的攀痊。由于每個(gè)方法對應(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)方法交換時(shí)遭殉,主要原理就是交換SEL和IMP的映射關(guān)系。
參考:http://www.reibang.com/p/46dd81402f63
http://www.reibang.com/p/adf0d566c887
http://www.reibang.com/p/13457a27624c