runtime是oc中一個比較底層的純c語言庫,包含了很多底層c語言的api
runtime其實和我們編程密切相關(guān),平時編寫的oc代碼中,在程序運(yùn)行時,其實最終都是轉(zhuǎn)成runtime的c語言代碼
可以這么理解,oc的底層實際上是在調(diào)用一個個c函數(shù),如何找到這些c函數(shù)的工作,就是由runtime去負(fù)責(zé)了
要理解runtime,首先要理解類的結(jié)構(gòu)
類的本質(zhì)是什么? 實際上就是一個類似結(jié)構(gòu)體的玩意,那么類里面有什么內(nèi)容呢,以下一張圖就能夠很清晰的理解

isa就是“is a”,對于所有繼承了NSObject的類其對象也都有一個isa指針。這個isa指針指向關(guān)于這個對象所屬的類的定義。?
任何直接或間接繼承了NSObject的類,它的實例對象(instacne objec)中都有一個isa指針,指向它的類對象(class object)。這個類對象(class object)中存儲了關(guān)于這個實例對象(instace object)所屬的類的定義的一切:包括變量,方法,遵守的協(xié)議等等。
以下這圖可以很清楚的表述上面的關(guān)系

簡單來說,一個類的對象的isa指針會指向這個類,這個類也有一個isa指針會指向這個類的元祖類(meta class)。
一個類以及其元祖類(meta class) 都會有一個super class 的指針指向其父類,一直到根類(NSObject),NSObject的元祖類的super class 會指回到NSObject類,至于NSObject是沒有super class 的,其super class 指針會指向nil
至于類和元祖類是什么關(guān)系呢? 首先,他們都是對象,根本上都是objc_class對象,因此也有類對象和元祖類對象的一說。區(qū)別在于,類對象中包含了這個類的實例變量,實例方法的定義,而元祖類對象中包含類的方法的定義。
搞清楚對象和類的這層關(guān)系之后,要理解起來就輕松多了,你可以理解成isa 和 super class 就是一個門牌號或者路標(biāo),runtime就是根據(jù)這些路標(biāo)去找到相應(yīng)的靜態(tài)方法和變量的。
-------------------------------------- 華麗的分割線 ? ----------------------------------
搞清楚指針之后,之后就看重點(diǎn)了
ivars(成員變量列表)
? ? ? ? 這個屬性 objc_ivar_list 結(jié)構(gòu)體類型,其實就是一個鏈表,存儲多個objc_ivar,而objc_ivar又是一個結(jié)構(gòu)體,用來存儲類的單個成員變量的信息
? ? ? ? ?簡單粗暴一點(diǎn)理解,ivar 就是成員變量
objc_ivar里面又有什么內(nèi)容呢?見下圖

methodlists(方法列表)
? ? ? 同ivars一樣理解,實際上這兩個結(jié)構(gòu)體幾乎是一樣的,也是一個objc_method_list 的結(jié)構(gòu)體, 也是一個鏈表,存儲多個objc_method, ?而objc_method 又是一個結(jié)構(gòu)體,用來存儲類的某個方法信息
我們也可以來一張圖來展示objc_method的風(fēng)采

看到SEL估計都不陌生了,一個方法的SEL 其實是一個C的字符串,并且會在OC的runtime中注冊這個selector,這個操作一般是在加載到內(nèi)存的時候就完成了這一步注冊操作,即在+ load 的方法中
實際上我們平時調(diào)用方法,都不是一步到位,實際上是通過selector找到該方法,然后再找到這個方法的實現(xiàn)(IMP),IMP本質(zhì)上就是一個函數(shù)指針,指向c函數(shù)
舉個栗子方便理解:
[ a ?sayHello] ?---> 在a對象中通過其isa指針找到其類對象--->找到methodList ---> 根據(jù)"sayHello"這個selector找到對應(yīng)的方法 ---> 找到其對應(yīng)的IMP指針 ?---> ?c函數(shù)
以上僅僅是方便理解而已,因為實際上,上面調(diào)用方法的環(huán)節(jié)還要加入緩存池的操作(cache)
Cache(方法緩存池)
? ? ? ? ? 二話不說,先上圖

? ? ? ? ? ?Cache其實就是一個存儲Method的鏈表,主要是為了優(yōu)化方法調(diào)用的性能。當(dāng)調(diào)用一個方法的時候,會先去緩存池找,如果沒找到,再去methodLists查找
------------------------------------------又是華麗的分割線-----------------------------
消息發(fā)送
?當(dāng)調(diào)用一個方法時[a sayHello],其實是被編譯器轉(zhuǎn)化為
objc_msgSend ( id self, SEL op, ... )
1.根據(jù)a實例對象的isa找到其對應(yīng)的類對象
2.優(yōu)先在類對象中的cache查找sayHello方法,如果找不到,再到methodLists查找
3.如果找不到,通過super_class 指針向父類查找,如果找不到,找父類的父類,一直到NSObject
4.一旦找到了,則執(zhí)行IMP的實現(xiàn)
容錯機(jī)制
要是找不到怎么辦?一開始我也以為會拋出異常,崩潰,實際上在崩潰之前還有三次容錯機(jī)制進(jìn)行處理,按照以下順序:
? ? ?Method Resolution
? ? ?Fast Forwarding
? ? ?Normal Forwarding
上圖最直接:

Method Resolution
首先Objective-C在運(yùn)行時調(diào)用+ resolveInstanceMethod:或+ resolveClassMethod:方法,讓你添加方法的實現(xiàn)。如果你添加方法并返回YES,那系統(tǒng)在運(yùn)行時就會重新啟動一次消息發(fā)送的過程。
舉一個簡單例子,定義一個類Message,它主要定義一個方法sendMessage,下面就是它的設(shè)計與實現(xiàn):
@interface?Message?:?NSObject
-?(void)sendMessage:(NSString?*)word;
@end
@implementation?Message
-?(void)sendMessage:(NSString?*)word
{
NSLog(@"normal?way?:?send?message?=?%@",?word);
}
@end
如果我在viewDidLoad方法中創(chuàng)建Message對象并調(diào)用sendMessage方法:
-?(void)viewDidLoad?{
[super?viewDidLoad];
Message?*message?=?[Messagenew];
[message?sendMessage:@"Sam?Lau"];
}
控制臺會打印以下信息:
normal way : send message = Sam Lau
但現(xiàn)在我將原來sendMessage方法實現(xiàn)給注釋掉,覆蓋resolveInstanceMethod方法:
#pragma?mark?-?Method?Resolution
///?override?resolveInstanceMethod?or?resolveClassMethod?for?changing?sendMessage?method?implementation
+?(BOOL)resolveInstanceMethod:(SEL)sel
{
? ? if(sel?==?@selector(sendMessage:))?{
? ? ? ? class_addMethod([selfclass],?sel,?imp_implementationWithBlock(^(id?self,?NSString?*word)?{
? ? ? ? NSLog(@"method?resolution?way?:?send?message?=?%@",?word);
? ? ? ? }),"v@*");
? ? }
returnYES;
}
控制臺就會打印以下信息:
method resolution way : send message = Sam Lau
注意到上面代碼有這樣一個字符串"v@*,它表示方法的參數(shù)和返回值,詳情請參考Type Encodings。
如果resolveInstanceMethod方法返回NO,運(yùn)行時就跳轉(zhuǎn)到下一步:消息轉(zhuǎn)發(fā)(Message Forwarding)。
Fast Forwarding
如果目標(biāo)對象實現(xiàn)- forwardingTargetForSelector:方法,系統(tǒng)就會在運(yùn)行時調(diào)用這個方法,只要這個方法返回的不是nil或self,也會重啟消息發(fā)送的過程,把這消息轉(zhuǎn)發(fā)給其他對象來處理。否則,就會繼續(xù)Normal Fowarding。
繼續(xù)上面Message類的例子,將sendMessage和resolveInstanceMethod方法注釋掉,然后添加forwardingTargetForSelector方法的實現(xiàn):
#pragma?mark?-?Fast?Forwarding
-?(id)forwardingTargetForSelector:(SEL)aSelector
{
? ? if(aSelector?==?@selector(sendMessage:))?{
? ? return[MessageForwardingnew];
? ? }
? ? return nil;
}
此時還缺一個轉(zhuǎn)發(fā)消息的類MessageForwarding,這個類的設(shè)計與實現(xiàn)如下:
@interface?MessageForwarding?:?NSObject
-?(void)sendMessage:(NSString?*)word;
@end
@implementation?MessageForwarding
-?(void)sendMessage:(NSString?*)word
{
NSLog(@"fast?forwarding?way?:?send?message?=?%@",?word);
}
@end
此時,控制臺會打印以下信息:
fast forwarding way : send message = Sam Lau
這里叫Fast,是因為這一步不會創(chuàng)建NSInvocation對象,但Normal Forwarding會創(chuàng)建它,所以相對于更快點(diǎn)。
Normal Forwarding
如果沒有使用Fast Forwarding來消息轉(zhuǎn)發(fā),最后只有使用Normal Forwarding來進(jìn)行消息轉(zhuǎn)發(fā)。它首先調(diào)用methodSignatureForSelector:方法來獲取函數(shù)的參數(shù)和返回值,如果返回為nil,程序會Crash掉,并拋出unrecognized selector sent to instance異常信息。如果返回一個函數(shù)簽名,系統(tǒng)就會創(chuàng)建一個NSInvocation對象并調(diào)用-forwardInvocation:方法。
繼續(xù)前面的例子,將forwardingTargetForSelector方法注釋掉,添加methodSignatureForSelector和forwardInvocation方法的實現(xiàn):
#pragma?mark?-?Normal?Forwarding
-?(NSMethodSignature?*)methodSignatureForSelector:(SEL)aSelector
{
? ? NSMethodSignature?*methodSignature?=?[super?methodSignatureForSelector:aSelector];
? ? if(!methodSignature)?{
? ? ? ? methodSignature?=?[NSMethodSignature?signatureWithObjCTypes:"v@:*"];
? ? }
? ? return methodSignature;
}
-?(void)forwardInvocation:(NSInvocation?*)anInvocation
{
? ? MessageForwarding?*messageForwarding?=?[MessageForwardingnew];
? ? if([messageForwarding?respondsToSelector:anInvocation.selector])?{
? ? ? ? [anInvocation?invokeWithTarget:messageForwarding];
? ? }
}
-----------------------------------華麗的分割線-------------------------------------------
結(jié)束語: runtime是個好東西,雖然本人平時日常開發(fā)很少用到,但是了解其原理以及底層結(jié)構(gòu),會讓你對整個oc的運(yùn)作有一個更好的理解
最后,本文章也是參考劉耀柱大神的文章寫出來的,更多關(guān)于runtime的干貨,在下面鏈接:
http://www.csdn.net/article/2015-07-06/2825133-objective-c-runtime/6