OC消息機(jī)制

基于Runtime的動(dòng)態(tài)特性

在蘋(píng)果的官方文檔中,對(duì)Runtime的介紹如下:

????The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work.????

ObjC?面向Runtime的語(yǔ)言真屯,它會(huì)盡可能地把決策從編譯時(shí)和連接時(shí)推遲到運(yùn)行時(shí)(簡(jiǎn)單來(lái)說(shuō)砾嫉,就是編譯后的文件不全是機(jī)器指令,還有一部分中間代碼手幢,在運(yùn)行的時(shí)候歌溉,通過(guò)Runtime再把需要轉(zhuǎn)換的中間代碼在翻譯成機(jī)器指令)這使得ObjC有著很大的靈活性。比如:

1瓶颠、動(dòng)態(tài)的確定類(lèi)型

2拟赊、我們可以動(dòng)態(tài)的確定消息傳遞的對(duì)象

3、動(dòng)態(tài)的給對(duì)象增加方法的實(shí)現(xiàn)?等等

那么粹淋,ObjC是如何實(shí)現(xiàn)這一靈活性吸祟?

ObjC Runtime庫(kù)是開(kāi)源的,可從這里下載 http://opensource.apple.com/


基于Runtime的消息傳遞機(jī)制


ObjC之所以說(shuō)是面向Runtime的語(yǔ)言桃移,最重要的特征是其消息傳遞機(jī)制屋匕。

什么是消息傳遞?消息傳遞不就和C語(yǔ)言的函數(shù)調(diào)用一個(gè)意思么借杰。过吻。

在C語(yǔ)言中,我們調(diào)用函數(shù)時(shí)蔗衡,必須先聲明函數(shù)(或者自上而下)纤虽,而實(shí)際上,聲明函數(shù)就是獲取函數(shù)地址绞惦,調(diào)用函數(shù)就是直接跳到地址執(zhí)行逼纸,代碼在被編譯器解析、優(yōu)化后翩隧,便是成為一堆匯編代碼樊展,然后連接各種庫(kù),完了生成可執(zhí)行的代碼(即是靜態(tài)的)堆生。

在ObjC中专缠,首先要搞清楚為什么不用Function Call 而用 Messaging?呢?一般調(diào)用函數(shù)(方法)淑仆,講的就是object對(duì)象涝婉,比如說(shuō):

person.say();

這里表示的就是person對(duì)象調(diào)用了say函數(shù)

而Messaging則是從Runtime的角度上 比如說(shuō)

? [receiver doSomething];

編譯后,則是成

???objc_msgsend(receiver @selector(doSomething));

?正確的理解應(yīng)該是蔗怠,表示你需要向receiver發(fā)送一個(gè)消息(doSomething)墩弯,而此時(shí)receiver不一定調(diào)用了doSomething這個(gè)方法曲梗,(ObjC中給任意對(duì)象發(fā)送任意消息檐薯,編譯時(shí)是可以通過(guò)了)只有到了運(yùn)行時(shí)女嘲,才會(huì)先去receiver是否去響應(yīng)這個(gè)消息万栅,在決策時(shí)執(zhí)行這個(gè)方法琼蚯,還是其他方法奴饮,或者轉(zhuǎn)發(fā)給其他對(duì)象薄坏。另外燕偶,在ObjC中,可以向nil發(fā)送消息任意方法不會(huì)Crash旺韭。

假設(shè)我們有兩個(gè)類(lèi):Human類(lèi)和Animal類(lèi)氛谜,其中Human類(lèi)有sayHello方法

Human *person = [[Human alloc] init];

Human *xiongmao = [[Animal alloc] init];

[person sayHello];

[xiongmao sayHello];

我們給person和xiongmao都發(fā)送了sayHello消息,而實(shí)際上区端,xiongmao這個(gè)對(duì)象時(shí)不響應(yīng)sayHello這個(gè)方法的值漫。而此時(shí)編譯器卻沒(méi)有給出警告和錯(cuò)誤信息,因此织盼,LLVM的Clang編譯器在編譯時(shí)杨何,只是簡(jiǎn)單的進(jìn)行語(yǔ)法分析,消息的發(fā)送實(shí)際上是在運(yùn)行時(shí)才執(zhí)行的沥邻。

而在運(yùn)行時(shí)晚吞,xiongmao這個(gè)對(duì)象指向的是Animal這個(gè)類(lèi),因?yàn)锳nimal這個(gè)類(lèi)沒(méi)有sayHello的執(zhí)行方法谋国,這個(gè)時(shí)候槽地,編譯器才報(bào)錯(cuò),程序崩潰芦瘾。

我們得出的結(jié)論是:

1捌蚊、消息的發(fā)送是在runtime時(shí)執(zhí)行的

2、編譯時(shí)近弟,編譯器只是簡(jiǎn)單的進(jìn)行語(yǔ)法分析缅糟,比如對(duì)應(yīng)的類(lèi)有沒(méi)有響應(yīng)的方法(實(shí)際上,對(duì)象是否是類(lèi)的實(shí)例祷愉,編譯器此時(shí)無(wú)法確定)

在發(fā)送以下方法時(shí)窗宦,都是在運(yùn)行時(shí)動(dòng)態(tài)判斷的:

respondsToSelector:

isKindOfClass: / isMemberOfClass:

instancesRespondToSelector:

conformToProtocol:

等等

消息傳遞的實(shí)現(xiàn)機(jī)制

那么,ObjC是如何基于Runtime實(shí)現(xiàn)消息傳遞機(jī)制二鳄,消息傳遞機(jī)制又是怎樣的赴涵。

先來(lái)了解第一個(gè)概念meta-class

我們?cè)谶\(yùn)行時(shí)創(chuàng)建一個(gè)NSError的子類(lèi)并為它添加一個(gè)方法:

Class newClass = objc_allocateClassPair([NSError class],"RuntimeErrorSubclass",0);

class_addMethod(newClass,@selector(report),(IMP)ReportFunction,"v@:");

objc_registerClassPair(newClass);

我們使用了Runtime庫(kù)中的objc_allocateClassPair為class pair創(chuàng)建了存儲(chǔ)空間,而這里的class pair語(yǔ)義上表示一對(duì)订讼,這里指的就是類(lèi)對(duì)象和元類(lèi)髓窜。

我們發(fā)現(xiàn),上面的newClass在創(chuàng)建時(shí)欺殿,也是用了allocate分配存儲(chǔ)空間寄纵,這就說(shuō)明了,類(lèi)實(shí)際上也是個(gè)對(duì)象脖苏。

什么是Class程拭,在ObjC中,每個(gè)Class實(shí)際上都有兩個(gè)Class棍潘,the Class object (類(lèi)對(duì)象)和meta Class(元類(lèi))恃鞋, the Class object 定義了instance method屋吨,metaClass 定義了class method,所以山宾,每個(gè)Class對(duì)象實(shí)際上是metaClass的一個(gè)單例。這里的類(lèi)對(duì)象是編譯器為每個(gè)類(lèi)生成的有且只有一個(gè)的單例鳍徽。而這個(gè)單例的isa指針指的是metaClass资锰。因此,對(duì)應(yīng)類(lèi)的內(nèi)存布局的理解是:

1阶祭、每個(gè)類(lèi)實(shí)際上都有類(lèi)對(duì)象和元類(lèi)兩個(gè)概念

2绷杜、類(lèi)對(duì)象是編譯器為每個(gè)類(lèi)生成的有且只有一個(gè)的保存關(guān)于實(shí)例方法的信息的單例

3、元類(lèi)則是保存類(lèi)方法信息的

4濒募、類(lèi)對(duì)象時(shí)元類(lèi)對(duì)象的一個(gè)單例 鞭盟,類(lèi)對(duì)象的isa指針指向元類(lèi)

5、類(lèi)對(duì)象和元類(lèi)都是基于objc_class結(jié)構(gòu)的

typedef struct objc_class *Class;

struct objc_class

{

????Class isa;

????Class super_class;???? //父類(lèi)

????const char* name;????//類(lèi)名

????long version;????????????//版本信息

????long info;???????????????? //類(lèi)信息

????long instance_size;????????????????????????????????????//實(shí)例大小

????struct objc_ivar_list *ivars;????????????????????????//實(shí)例參數(shù)鏈表

????struct objc_method_list *methodLists;??? ?//方法鏈表

????struct objc_cache *cache;?????????????????????? ??//方法緩存

????struct objc_protocol_list *protocols;????????//協(xié)議鏈表

}?

而NSObject的結(jié)構(gòu)是:

?struct NSObject{

????Class?*isa

}

在objc中每個(gè)實(shí)例對(duì)象都有objc_class結(jié)構(gòu)體的指針isa指向其類(lèi)的類(lèi)對(duì)象瑰剃,而其類(lèi)的類(lèi)對(duì)象的isa指針指向其類(lèi)的元類(lèi)對(duì)象齿诉,該類(lèi)的元類(lèi)對(duì)象的isa指針指向NSObject的元類(lèi)對(duì)象。NSObject的元類(lèi)對(duì)象指向其類(lèi)對(duì)象晌姚。

至于super_class就不用說(shuō)了吧粤剧。。

具體的內(nèi)存布局挥唠,可以參考此文章:http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html

從ObjC的內(nèi)存布局可以知道抵恋,通過(guò)isa指針,對(duì)象可以訪問(wèn)它對(duì)應(yīng)的類(lèi)的信息和相應(yīng)的父類(lèi)的信息宝磨,而消息機(jī)制便是通過(guò)isa指針來(lái)實(shí)現(xiàn)動(dòng)態(tài)特性的弧关。

上面的方法鏈表中,里面存儲(chǔ)的是Method類(lèi)型的唤锉,這里有Method世囊、SEL、IMP三個(gè)概念


typedef struct objc_method *Method

typedef struct?objc_method{

????SEL method_name;????

????char *method_type;

????IMP method _imp;

};

SEL表示方法的簽名窿祥,一般SEL = @selector();

IMP?表示函數(shù)指針茸习,其定義:

typedef id(*IMP)(id,SEL,..)

前面兩個(gè)是函數(shù)指針的對(duì)象和方法簽名,后面就是函數(shù)的參數(shù)壁肋。 比如

void(*setName_Func)(id, SEL, NSString*);

setName_Func = (void(*)(id,SEL,NSString))[receiver methodForSelecor:@selector(setName:)];? //返回name的函數(shù)指針

setName_Func(receiver,@selector(setName:),Liming); //通過(guò)函數(shù)指針調(diào)用函數(shù)

運(yùn)行時(shí)消息的傳遞機(jī)制

在ObjC中号胚,消息只有在運(yùn)行時(shí)才被綁定到方法的執(zhí)行中,我們發(fā)送消息時(shí)浸遗,使用的是[]猫胁,而在運(yùn)行時(shí),被ObjC的Runtime庫(kù)編譯成

objc_msgsend(receiver ,selector, arg1, arg2,...)?

這個(gè)函數(shù)給動(dòng)態(tài)綁定做了以下工作:

1跛锌、首先弃秆,它會(huì)根據(jù)給定的selector届惋,找到相關(guān)的procedure(執(zhí)行方法),因?yàn)镺bjC的多態(tài)性菠赚,所以尋找其procedure是根據(jù)receiver的類(lèi)

2脑豹、call the procedure,并且把相關(guān)的arg1衡查,arg2等參數(shù)傳給它

3瘩欺、返回procedure的返回值。

傳遞消息的關(guān)鍵點(diǎn)是編譯器為每個(gè)對(duì)象和類(lèi)所build的結(jié)構(gòu)拌牲,這個(gè)結(jié)構(gòu)包括:

1俱饿、superclass的指針

2、類(lèi)的分派表塌忽,這個(gè)類(lèi)的分派表包含了相關(guān)類(lèi)的方法的地址

這個(gè)結(jié)構(gòu)由對(duì)象和類(lèi)的isa指針指向

(實(shí)際上拍埠,這里和我們上面討論的內(nèi)存布局相似)

蘋(píng)果文檔關(guān)于運(yùn)行時(shí)執(zhí)行方法綁定的流程概述

?When a message is sent to an object, the messaging function follows the object’s isa pointer to the class

structure where it looks up the method selector in the dispatch table. If it can’t find the selector there,

objc_msgSend follows the pointer to the superclass and tries to find the selector in its dispatch table. Successive

failures cause objc_msgSend to climb the class hierarchy until it reaches the NSObject class. Once it locates

the selector, the function calls the method entered in the table and passes it the receiving object’s data structure.

上面講的是關(guān)于消息的綁定機(jī)制,而在運(yùn)行時(shí)土居,還存在消息的轉(zhuǎn)發(fā)機(jī)制

前面我們講過(guò)枣购,在ObjC中,發(fā)送消息給一個(gè)不響應(yīng)這個(gè)方法的對(duì)象擦耀,是合法的坷虑。之所以設(shè)計(jì)這種機(jī)制的原因,就是來(lái)模擬多重繼承(ObjC中是不支持多重繼承的)埂奈。轉(zhuǎn)發(fā)機(jī)制是Runtime非常重要的特性迄损,其機(jī)制大概如下:

1、Runtime首先會(huì)進(jìn)行消息綁定账磺,通過(guò)父類(lèi)的cache和分發(fā)表來(lái)綁定消息的執(zhí)行方法

2芹敌、如果沒(méi)有,運(yùn)行時(shí)會(huì)給類(lèi)的對(duì)象發(fā)送+(BOOL)resolveInstanceMethod:(SEL)Name消息垮抗,這個(gè)消息的執(zhí)行方法允許你在運(yùn)行時(shí)給該類(lèi)增加執(zhí)行方法氏捞。如果消息返回的是YES,則會(huì)redispatch消息(就是對(duì)消息在進(jìn)行綁定)

3冒版、如果2步驟還不行液茎,Runtime會(huì)發(fā)送-(id)forwardingTargetForSelector:(SEL)aSelector,這個(gè)消息返回另外一個(gè)能響應(yīng)該消息的對(duì)象辞嗡,然后把消息轉(zhuǎn)發(fā)給別的對(duì)象

4捆等、如果3步驟返回的對(duì)象是nil,或者是self续室,它會(huì)發(fā)送- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector?栋烤,這里會(huì)返回一個(gè)方法簽名用于Invocation,即-(void)forwardInvocation:(NSInvocation *)anInvocation.通過(guò)這個(gè)方法挺狰,你可以把消息轉(zhuǎn)發(fā)給任意擁有相應(yīng)執(zhí)行方法的類(lèi)(其實(shí)就是模擬多重繼承)

5明郭、如果4步驟都不行买窟,運(yùn)行時(shí)就會(huì)發(fā)送消息?- (void)doesNotRecognizeSelector:(SEL)aSelector?給你的對(duì)象,執(zhí)行這個(gè)方法將會(huì)拋出一個(gè)異常薯定,然后程序就崩潰了始绍。。

原文:https://blog.csdn.net/dan_163/article/details/38268957

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末话侄,一起剝皮案震驚了整個(gè)濱河市亏推,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌满葛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罢屈,死亡現(xiàn)場(chǎng)離奇詭異嘀韧,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)缠捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)锄贷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人曼月,你說(shuō)我怎么就攤上這事谊却。” “怎么了哑芹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵炎辨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我聪姿,道長(zhǎng)碴萧,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任末购,我火速辦了婚禮破喻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盟榴。我一直安慰自己曹质,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布擎场。 她就那樣靜靜地躺著羽德,像睡著了一般。 火紅的嫁衣襯著肌膚如雪迅办。 梳的紋絲不亂的頭發(fā)上玩般,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音礼饱,去河邊找鬼坏为。 笑死究驴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匀伏。 我是一名探鬼主播洒忧,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼够颠!你這毒婦竟也來(lái)了熙侍?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤履磨,失蹤者是張志新(化名)和其女友劉穎蛉抓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體剃诅,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巷送,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矛辕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笑跛。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖聊品,靈堂內(nèi)的尸體忽然破棺而出飞蹂,到底是詐尸還是另有隱情,我是刑警寧澤翻屈,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布陈哑,位于F島的核電站,受9級(jí)特大地震影響伸眶,放射性物質(zhì)發(fā)生泄漏芥颈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一赚抡、第九天 我趴在偏房一處隱蔽的房頂上張望爬坑。 院中可真熱鬧,春花似錦涂臣、人聲如沸盾计。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)署辉。三九已至,卻和暖如春岩四,著一層夾襖步出監(jiān)牢的瞬間哭尝,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工剖煌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留材鹦,地道東北人逝淹。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桶唐,于是被迫代替她去往敵國(guó)和親栅葡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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