iOS內(nèi)功篇:runtime

前言

本來打算寫一篇關(guān)于runtime的學(xué)習(xí)總結(jié),無奈長篇大論不是我的風(fēng)格,就像寫申論一樣痛苦,加之網(wǎng)上關(guān)于tuntime的文章多如牛毛,應(yīng)該也夠童子們學(xué)習(xí)的了,今天就隨便聊聊我的理解吧。

runtime是什么

對于初學(xué)者,runtime如尼斯湖水怪一樣,只存在于傳說中,對于開發(fā)者,runtime是做好iOS開發(fā),或是深刻掌握Objective C所必需理解的東西。大公司面試都喜歡問:你對runtime熟悉嗎?并不是runtime在開發(fā)中經(jīng)常用到,我認為它是OC最核心的部分,只有掌握好它,你才能理解其底層的原理,而不是做一個只會造輪子的碼農(nóng)。要練成蓋世神功,需先奠定自身深厚的內(nèi)功,而tuntime就是iOS開發(fā)中的內(nèi)功。

那么runtime到底是什么鬼?

runtime是一個c和匯編寫的動態(tài)庫(感謝Lision的指正),它就像一個小小的系統(tǒng),將OC和C緊密關(guān)聯(lián),這個系統(tǒng)主要做兩件事 :
1、封裝C語言的結(jié)構(gòu)體和函數(shù),讓開發(fā)者在運行時創(chuàng)建、檢查或者修改類、對象和方法等等。
2、傳遞消息,找出方法的最終執(zhí)行代碼。

聽起來蠻抽象的,我們來點通俗的吧?沒問題~~
我們先寫一句OC的代碼

[zhangsan walkTheDog];

那么在運行時runtime會將它轉(zhuǎn)化成C語言的代碼

objc_msgSend(zhangsan, @selector(walkTheDog));

這個方法就是發(fā)送消息的方法,類似這樣的方法runtime提供了很多,比如:

objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount ); // 獲取屬性列表
Method * class_copyMethodList ( Class cls, unsigned int *outCount );            // 獲取所有方法的數(shù)組
BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );       // 添加方法

那么我們可以利用這些方法干點什么?

1、遍歷對象的屬性
比如,看看zhangsan的有哪些屬性(身高:180、年齡:18)
2、動態(tài)添加/修改屬性,動態(tài)添加/修改/替換方法
比如,修改zhangsan的身高為190、年齡為20,替換walkTheDog方法(變成walkTheBigDog),給他添加一個新方法(walkTheCat)等等
3、動態(tài)創(chuàng)建類/對象/協(xié)議等等
比如,創(chuàng)建一個新的對象:lisi
4、方法攔截調(diào)用
比如,給zhangsan發(fā)送一個walkTheDog消息,但是zhangsan不知道怎么walk?。]實現(xiàn)該方法),那我們可以攔截下,給該方法動態(tài)添加一個實現(xiàn),甚至可以講該方法定向或者打包給lisi(其他對象),讓lisi來walk。

以上就是runtime的通俗解釋,只是稍微舉個例子,更多用法大家可以發(fā)揮聰明才智,舉一反三。

方法調(diào)用流程

通俗地講,調(diào)用方法(包含實例方法和類方法)相當于給一個對象發(fā)送消息。

所以,實際上,類本身也是一個對象(關(guān)于Class這一塊就不再這里展開了)。
當我們調(diào)用一個方法時,是這樣的:
Instance:調(diào)用實例方法時,會到對象所屬的類的方法列表中查找。
Class:調(diào)用類方法時,會到類的metaClass的方法列表中查找。

下面以實例對象調(diào)用方法[blackDog walk]為例描述方法調(diào)用的流程:

1、編譯器會把`[blackDog walk]`轉(zhuǎn)化為`objc_msgSend(blackDog,SEL)`,SEL為@selector(walk)。
2、Runtime會在blackDog對象所對應(yīng)的Dog類的方法緩存列表里查找方法的SEL
3、如果沒有找到,則在Dog類的方法分發(fā)表查找方法的SEL。(類由對象isa指針指向,方法分發(fā)表即methodList)
4、如果沒有找到,則在其父類(設(shè)Dog類的父類為Animal類)的方法分發(fā)表里查找方法的SEL(父類由類的superClass指向)
5、如果沒有找到,則沿繼承體系繼續(xù)下去,最終到達NSObject類。
6、如果在234的其中一步中找到,則定位了方法實現(xiàn)的入口,執(zhí)行具體實現(xiàn)
7、如果最后還是沒有找到,會面臨兩種情況:
``(1) 如果是使用`[blackDog walk]`的方式調(diào)用方法``
``(2) 使用`[blackDog performSelector:@selector(walk)]`的方式調(diào)用方法``

第一種情況編譯器會報錯,第二種需要到運行時才能確定對象能否接收指定的消息,這時候會進入消息轉(zhuǎn)發(fā)的流程:

消息轉(zhuǎn)發(fā)流程

1、動態(tài)方法解析
接收到未知消息時(假設(shè)blackDog的walk方法尚未實現(xiàn)),runtime會調(diào)用+resolveInstanceMethod:(實例方法)或者+resolveClassMethod:(類方法)
在該方法中,我們可以給未知消息新增一個已經(jīng)實現(xiàn)了的方法。

void walkFunc(id self, SEL _cmd) {
    //let the dog walk
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSString * selString = NSStringFromSelector(sel);
    if ([selString isEqualToString:@"walk"]) {
        class_addMethod(self.class, @selector(walk), (IMP)walkFunc, "@:");
    }
    return [super resolveInstanceMethod:sel];
}

2、備用接收者
如果以上方法沒有做處理,runtime會調(diào)用- (id)forwardingTargetForSelector:(SEL)aSelector方法。
如果該方法返回了一個非nil(也不能是self)的對象,而且該對象實現(xiàn)了這個方法,那么這個對象就成了消息的接收者,消息就被分發(fā)到該對象。
適用情況:通常在對象內(nèi)部使用,讓內(nèi)部的另外一個對象處理消息,在外面看起來就像是該對象處理了消息。
比如:blackDog讓女朋友whiteDog來接收這個消息

- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSString * selString = NSStringFromSelector(aSelector);
    if ([selString isEqualToString:@"walk"]) {
        return self.whiteDog;
    }
    return [super forwardingTargetForSelector:aSelector];
}

3、完整消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)anInvocation方法中選擇轉(zhuǎn)發(fā)消息的對象,其中anInvocation對象封裝了未知消息的所有細節(jié),并保留調(diào)用結(jié)果發(fā)送到原始調(diào)用者。
比如:blackDog將消息完整轉(zhuǎn)發(fā)給主人dogOwner來處理

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([DogOwner instancesRespondToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self.dogOwner];
    }
}

4、如果在以上三個方法都沒有處理未知消息,則會引發(fā)異常。

后記

本文主要通俗地描述了runtime的概念,并對其主要作用做了簡單的概括,旨在給讀者拋磚引玉,runtime的奧妙之處就由讀者多多探索學(xué)習(xí)了。
初學(xué)者需要更深入地學(xué)習(xí):
1、基本概念:Class、Ivar、Method等等
2、消息轉(zhuǎn)發(fā)機制
3、在<objc/runtime.h>中理解runtime提供的方法和功能
4、在實際開發(fā)中如何靈活運用runtime

^_^相對于理論,程序員總是更擅長用代碼來表達,博主接下來將陸續(xù)更新runtime實戰(zhàn)應(yīng)用系列文章,敬請關(guān)注。

傳送門:
iOS runtime實戰(zhàn)應(yīng)用:成員變量和屬性
iOS runtime實戰(zhàn)應(yīng)用:關(guān)聯(lián)對象
iOS runtime實戰(zhàn)應(yīng)用:Method Swizzling

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 文中的實驗代碼我放在了這個項目中。 以下內(nèi)容是我通過整理[這篇博客] (http://yulingtianxia....
    茗涙閱讀 1,028評論 0 6
  • 本文轉(zhuǎn)載自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex閱讀 888評論 0 1
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,892評論 33 466
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 835評論 0 2
  • 傳送門:http://www.itdecent.cn/p/f493bc6a949e/comments/168898...
    永遠都能閱讀 216評論 0 0

友情鏈接更多精彩內(nèi)容