runtime小序曲慎王,從運(yùn)行時(shí)多態(tài)看這股神秘力量

runtime一直很神秘,本文從面向?qū)ο蟮幕咎匦远鄳B(tài)宏侍,引出runtime柬祠,并做了基本的解釋和簡(jiǎn)單應(yīng)用梳理。后續(xù)會(huì)深入runtime负芋。
學(xué)習(xí)進(jìn)度:

一锨天、多態(tài)

多態(tài)(英語(yǔ):polymorphism)毯盈,是指計(jì)算機(jī)程序運(yùn)行時(shí),相同的消息可能會(huì)送給多個(gè)不同的類(lèi)別之對(duì)象病袄,而系統(tǒng)可依據(jù)對(duì)象所屬類(lèi)別搂赋,引發(fā)對(duì)應(yīng)類(lèi)別的方法,而有不同的行為益缠。簡(jiǎn)單來(lái)說(shuō)脑奠,所謂多態(tài)意指相同的消息給予不同的對(duì)象會(huì)引發(fā)不同的動(dòng)作稱(chēng)之。
多態(tài)可分為靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài):

  • 靜態(tài)多態(tài)幅慌,允許將不同的特殊行為和單個(gè)泛化記號(hào)相關(guān)聯(lián)宋欺,由于這種關(guān)聯(lián)處理于編譯期而非運(yùn)行期,因此被稱(chēng)為“靜態(tài)”胰伍,比如函數(shù)重載齿诞。
  • 動(dòng)態(tài)多態(tài),也即運(yùn)行時(shí)多態(tài)骂租,對(duì)于C++來(lái)說(shuō)祷杈,通過(guò)類(lèi)繼承機(jī)制和虛函數(shù)機(jī)制生效于運(yùn)行期,可以?xún)?yōu)雅地處理異質(zhì)對(duì)象集合渗饮,只要其共同的基類(lèi)定義了虛函數(shù)的接口但汞;對(duì)于OC來(lái)說(shuō),通過(guò)借鑒于smallTalk的消息傳遞機(jī)制實(shí)現(xiàn)動(dòng)態(tài)多態(tài)抽米。一般來(lái)說(shuō)特占,動(dòng)態(tài)多態(tài)才是真正的多態(tài)。

那么云茸,接下來(lái)是目,分別看一下C++和OC對(duì)動(dòng)態(tài)多態(tài)的代碼實(shí)現(xiàn)。

二标捺、C++懊纳、OC多態(tài)對(duì)比

1揉抵、C++動(dòng)態(tài)多態(tài)實(shí)現(xiàn)

代碼

#include<iostream>  
using namespace std;  
class Base  
{  
public:  
    virtual void f(float x)  
    {  
        cout<<"Base::f(float)"<< x <<endl;  
    }  
    void g(float x)  
    {  
        cout<<"Base::g(float)"<< x <<endl;  
    }
};  
class Derived : public Base  
{  
public:  
    virtual void f(float x)  
    {  
        cout<<"Derived::f(float)"<< x <<endl;
    }  
    void g(int x)  
    {  
        cout<<"Derived::g(int)"<< x <<endl;
    } 
};  
int main(void)  
{  
    Derived d;  
    Base *pb = &d;  
    Derived *pd = &d;  
    pb->f(3.14f);   // 輸出:Derived::f(float) 3.14  
    pd->f(3.14f);   // 輸出:Derived::f(float) 3.14  
    pb->g(3.14f);   // 輸出:Base::g(float)  3.14  
    pd->g(3.14f);   // 輸出:Derived::g(int) 3.14
    return 0;  
}  

分析
上述代碼創(chuàng)建了Derived對(duì)象d,將Base類(lèi)型指針pb和Derived類(lèi)型指針pd指向d嗤疯,可知pb和pd的編譯時(shí)刻的類(lèi)型分別是Base和Derivevd冤今,而運(yùn)行時(shí)刻類(lèi)型均為Derived∶浚看上述四個(gè)輸出戏罢,pd的輸出沒(méi)有什么可說(shuō)的,只是為了對(duì)比脚囊。重點(diǎn)是pb的f龟糕、g兩個(gè)函數(shù)的輸出,可見(jiàn)f為其運(yùn)行時(shí)刻類(lèi)型的輸出結(jié)果悔耘,g為編譯時(shí)刻的輸出結(jié)果讲岁,而兩者的區(qū)別即為f是虛函數(shù)。所以衬以,可以得出結(jié)論缓艳,對(duì)于C++來(lái)說(shuō),虛函數(shù)完成了其動(dòng)態(tài)多態(tài)的實(shí)現(xiàn)看峻。

2阶淘、OC動(dòng)態(tài)多態(tài)實(shí)現(xiàn)

代碼

#import <Foundation/Foundation.h>
@interface Base: NSObject
@end
@implementation Base
- (void)f {
    NSLog(@"Base f");
}
@end
@interface Derived: Base
@end
@implementation Derived
- (void)f {
    NSLog(@"Derived f");
}
@end

int main(int argc, char *argv[]) {
    Derived *d = [[Derived alloc] init];
    Base *pb = d;
    Derived *pd = d;
    [pb f];     // 輸出:Derived f
    [pd f];     // 輸出:Derived f
    return 0;
}

分析
和C++類(lèi)似,上述代碼創(chuàng)建了Derived對(duì)象d备籽,將Base類(lèi)型指針pb和Derived類(lèi)型指針pd指向d舶治,可知pb和pd的編譯時(shí)刻的類(lèi)型分別是Base和Derived分井,而運(yùn)行時(shí)刻類(lèi)型均為Derived车猬。從兩個(gè)輸出來(lái)看,可以發(fā)現(xiàn)尺锚,OC并沒(méi)有使用虛函數(shù)珠闰,也沒(méi)有虛函數(shù)的概念,但是OC自動(dòng)完成了動(dòng)態(tài)多態(tài)的實(shí)現(xiàn)瘫辩。

疑問(wèn)
是何種神秘力量幫我們完成了動(dòng)態(tài)多態(tài)伏嗜?請(qǐng)往下看。

三伐厌、運(yùn)行時(shí)機(jī)制

C語(yǔ)言是一種靜態(tài)語(yǔ)言承绸,而OC是一個(gè)依托于C的語(yǔ)言。為了保持了C編譯時(shí)的功能(如類(lèi)型檢查)挣轨,并且增加靈活性军熏,OC在C的基礎(chǔ)上,借鑒了smalltalk的消息傳遞機(jī)制為其添加了運(yùn)行時(shí)機(jī)制卷扮,這也就是runtime機(jī)制荡澎。
換種方式來(lái)說(shuō)均践,runtime機(jī)制是一個(gè)由C和匯編編寫(xiě)的Library,而這個(gè)Library給C增加了面向?qū)ο蟮奶匦阅︶#瑥亩纬闪薕C語(yǔ)言彤委。

基于runtime,OC中對(duì)象的類(lèi)型和對(duì)象所執(zhí)行的方法都是在運(yùn)行時(shí)階段進(jìn)行查找并確認(rèn)的或衡,這種機(jī)制被稱(chēng)為動(dòng)態(tài)綁定焦影。也就是上述的神秘力量。

OC中與運(yùn)行時(shí)機(jī)制進(jìn)行交互的方面大致以下三種:
1封断、Objective-C源代碼
大部分情況下你就只管寫(xiě)你的Objc代碼就行偷办,runtime 系統(tǒng)自動(dòng)在幕后辛勤勞作著。比如二中的下述代碼:

[pb f]; 

實(shí)質(zhì)是(clang轉(zhuǎn)成cpp后):

((void (*)(id, SEL))(void *)objc_msgSend)((id)pb, sel_registerName("f"));

2澄港、NSObject的方法
Cocoa 中大多數(shù)類(lèi)都繼承于NSObject類(lèi)椒涯,也就自然繼承了它的方法。有的NSObject中的方法起到了抽象接口的作用回梧,比如description方法需要你重載它并為你定義的類(lèi)提供描述內(nèi)容废岂。NSObject還有些方法能在運(yùn)行時(shí)獲得類(lèi)的信息,并檢查一些特性狱意,比如class返回對(duì)象的類(lèi)湖苞;isKindOfClass:和isMemberOfClass:則檢查對(duì)象是否在指定的類(lèi)繼承體系中;respondsToSelector:檢查對(duì)象能否響應(yīng)指定的消息详囤;conformsToProtocol:檢查對(duì)象是否實(shí)現(xiàn)了指定協(xié)議類(lèi)的方法财骨;methodForSelector:則返回指定方法實(shí)現(xiàn)的地址。

3藏姐、Runtime的函數(shù)
Runtime 系統(tǒng)是一個(gè)由一系列函數(shù)和數(shù)據(jù)結(jié)構(gòu)組成恩闻,具有公共接口的動(dòng)態(tài)共享庫(kù)。頭文件存放于/usr/include/objc目錄下捶码。許多函數(shù)允許你用純C代碼來(lái)重復(fù)實(shí)現(xiàn) Objc 中同樣的功能窿吩。雖然有一些方法構(gòu)成了NSObject類(lèi)的基礎(chǔ),但是你在寫(xiě) Objc 代碼時(shí)一般不會(huì)直接用到這些函數(shù)的兜材,除非是寫(xiě)一些 Objc 與其他語(yǔ)言的橋接或是底層的debug工作理澎。在Objective-C Runtime Reference中有對(duì) Runtime 函數(shù)的詳細(xì)文檔。

四曙寡、靜態(tài)語(yǔ)言和動(dòng)態(tài)語(yǔ)言對(duì)比

上文提過(guò)糠爬,C是一門(mén)靜態(tài)語(yǔ)言,而runtime為C增加了面向?qū)ο蟮奶匦跃偈⑶襯untime的消息傳遞方式給了這門(mén)語(yǔ)言一種動(dòng)態(tài)的特性执隧。下面就看看靜態(tài)和動(dòng)態(tài)的區(qū)別,以及我們?nèi)绾卫谩?/p>

對(duì)C來(lái)說(shuō)

//編譯前:
#include < stdio.h >
int main(int argc, const char **argv[])
{
        printf("Hello World!");
        return 0;
}

//編譯后
.text
 .align 4,0x90
 .globl _main
_main:
Leh_func_begin1:
 pushq %rbp
Llabel1:
 movq %rsp, %rbp
Llabel2:
 subq $16, %rsp
Llabel3:
 movq %rsi, %rax
 movl %edi, %ecx
 movl %ecx, -8(%rbp)
 movq %rax, -16(%rbp)
 xorb %al, %al
 leaq LC(%rip), %rcx
 movq %rcx, %rdi
 call _printf
 movl $0, -4(%rbp)
 movl -4(%rbp), %eax
 addq $16, %rsp
 popq %rbp
 ret
Leh_func_end1:
 .cstring
LC:
 .asciz "Hello World!"

這里不用關(guān)注一堆匯編代碼,只需要看最后一句殴玛,可得知捅膘,在編譯時(shí)刻,已經(jīng)確定了輸出結(jié)果滚粟。

對(duì)OC而言

//編譯前
[pb f];

//編譯后
((void (*)(id, SEL))(void *)objc_msgSend)((id)pb, sel_registerName("f"));

在編譯后寻仗,完全不知道會(huì)輸出什么,只知道會(huì)給對(duì)象pb凡壤,發(fā)送消息f署尤,即把一部分操作推到了運(yùn)行時(shí)。

當(dāng)然亚侠,OC并不是一個(gè)動(dòng)態(tài)語(yǔ)言曹体,只是給C添加了動(dòng)態(tài)特性。

而我們所謂的runtime應(yīng)用硝烂,也就是利用的這樣一個(gè)延遲箕别,即在運(yùn)行時(shí)做一些事情,下面簡(jiǎn)單介紹下應(yīng)用串稀。

五母截、runtime應(yīng)用

談了半天护蝶,也不知道runtime有什么鬼用途垦江。在三中我就提到了绽族,runtime為我們提供了運(yùn)行時(shí)刻操縱代碼的能力涛漂,接下來(lái),稍微說(shuō)下runtime的常見(jiàn)應(yīng)用间狂,不過(guò)多解釋?zhuān)纯淳秃煤渭Γ瑫?huì)在后續(xù)的篇目中進(jìn)行深入。

  • Swizzling,替換兩個(gè)方法的實(shí)現(xiàn),可以做一些埋點(diǎn)、容錯(cuò)等工作捆姜。
  • 關(guān)聯(lián)屬性磕仅,在運(yùn)行時(shí)刻為某個(gè)class增加屬性店茶。
  • Json映射,通過(guò)runtime的方法將Json映射到model族壳。

runtime功能很強(qiáng)大,這里只是幾個(gè)最常見(jiàn)的用法趣些,如果想了解更多內(nèi)容仿荆,請(qǐng)繼續(xù)關(guān)注我。

六喧务、文獻(xiàn)

1赖歌、https://zh.wikipedia.org/wiki/%E5%A4%9A%E5%9E%8B_(%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6)
2、http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/

本文僅僅從一個(gè)角度引出runtime功茴,后續(xù)會(huì)繼續(xù)用最通俗的大白話(huà)進(jìn)行runtime的講解庐冯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市坎穿,隨后出現(xiàn)的幾起案子展父,更是在濱河造成了極大的恐慌,老刑警劉巖玲昧,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栖茉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡孵延,警方通過(guò)查閱死者的電腦和手機(jī)吕漂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)尘应,“玉大人惶凝,你說(shuō)我怎么就攤上這事∪郑” “怎么了苍鲜?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)玷犹。 經(jīng)常有香客問(wèn)我混滔,道長(zhǎng),這世上最難降的妖魔是什么歹颓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任坯屿,我火速辦了婚禮,結(jié)果婚禮上晴股,老公的妹妹穿的比我還像新娘愿伴。我一直安慰自己,他們只是感情好电湘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般寂呛。 火紅的嫁衣襯著肌膚如雪怎诫。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,245評(píng)論 1 299
  • 那天贷痪,我揣著相機(jī)與錄音幻妓,去河邊找鬼。 笑死劫拢,一個(gè)胖子當(dāng)著我的面吹牛肉津,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舱沧,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼妹沙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了熟吏?” 一聲冷哼從身側(cè)響起距糖,我...
    開(kāi)封第一講書(shū)人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牵寺,沒(méi)想到半個(gè)月后悍引,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帽氓,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年趣斤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黎休。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浓领,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出奋渔,到底是詐尸還是另有隱情镊逝,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布嫉鲸,位于F島的核電站撑蒜,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏玄渗。R本人自食惡果不足惜座菠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望藤树。 院中可真熱鬧浴滴,春花似錦、人聲如沸岁钓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至品嚣,卻和暖如春炕倘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翰撑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工罩旋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人眶诈。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓涨醋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親逝撬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浴骂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容