runtime一直很神秘,本文從面向?qū)ο蟮幕咎匦远鄳B(tài)宏侍,引出runtime柬祠,并做了基本的解釋和簡(jiǎn)單應(yīng)用梳理。后續(xù)會(huì)深入runtime负芋。
學(xué)習(xí)進(jìn)度:
- runtime小序曲漫蛔,從運(yùn)行時(shí)多態(tài)看這股神秘力量
- runtime進(jìn)行曲,objc_msgSend的前世今生(一)
- runtime進(jìn)行曲旧蛾,objc_msgSend的前世今生(二)
- runtime變奏曲莽龟,那些藏在runtime中的接口(一)
- runtime變奏曲,那些藏在runtime中的接口(二)
一锨天、多態(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的講解庐冯。