详解Objective-C消息传递机制
Objective-C獲取消息工作機(jī)制是本文要介紹的內(nèi)容,看name?mangling的時(shí)候,也講到了Objective-C的name mangling,于是又重新讀了一下Objective-C?2.0?programming Language以及Objective-C 2.0?Runtime?Reference里的相關(guān)內(nèi)容,自己歸納一下??。
???MyClass.h ?
???@interface?MyClass?:?NSObject ?
???{ ?
????} ?
???@end ?
???MyClass.m ?
???#import?< span>usr/include/objc/runtime.h>?
???#import?“MyClass.h” ?
???void?myClassIMP(id?_rec,?SEL?_cmd,?int?theInt) ?
???{ ?
??????NSLog(@”dynamic?added?method:%d”,theInt); ?
???} ?
? ?
-?(id)init ?
{ ?
????if(?(?self?=?[super?init])?!=?nil?) ?
????{ ?
???class_addMethod([MyClass?class],?@selector(dynGeneratedMethod:),(IMP)myClassIMP,”v@:i”); ?
?????} ?
????return?self; ?
} ?
?
Main.c ?
#import?“MyClass.h” ?
int?main(int?argc,?char?*argv[]) ?
{ ?
??MyClass?theInstance?=?[[MyClass?alloc]?init]; ?
??[theInstance?dynGeneratedMethod:10]; ?
?return?0; ?
}?
這段代碼執(zhí)行的結(jié)果是在控制臺(tái)上輸出:
dynamic?added?method:10?
接著來詳細(xì)分析一下上面的代碼:
在ObjC的類中這樣的一個(gè)聲明?– (void)foo:(int)a;被稱作方法(method),而在調(diào)用的地方: [theClass foo:10];則被稱之為發(fā)送消息(send message),具體來說是給對(duì)象theClass 發(fā)送foo:消息,注意這里foo后面的”:”,它也是消息名稱的一部分,最前面的-代表實(shí)例方法,+代表類方法??。而類似的語句,在C或C++中,通常被稱為呼叫函數(shù)(call function),在ObjC中,函數(shù)(function)一詞很少用到,不是它不存在,而是它被ObjC runtime給隱藏了起來??。
???
如前所述,ObjC是以消息機(jī)制來工作的,但其實(shí)諸如-(void)foo:(int)a的語句在編譯時(shí)被objc_msgSend(receiver,selector,arg1,arg2,….)替換了,所以其實(shí)每一條發(fā)送消息的代碼本質(zhì)上還是調(diào)用函數(shù)(call function),不過他們調(diào)用的都是同一個(gè)函數(shù)objc_msgSend(也可能是objc_msgSend_stret(返回值是結(jié)構(gòu)體),objc_msgSend_fpret(返回值是浮點(diǎn)型)等)
分析objc_msgSend的參數(shù),第一個(gè)receiver的類型是id,代表接受消息的對(duì)象,第二個(gè)是selector代表接收對(duì)象的方法,后面的是該方法的參數(shù),之前那條語句的被編譯器替換后就是:
[theClass?foo:10]??->?objc_msg(theClass,@selector(foo:),10);?
因?yàn)橄⒌慕邮軐?duì)象和接受對(duì)象的方法都參數(shù)化,所以在運(yùn)行時(shí)刻,接受對(duì)象和接受對(duì)象的方法都可以是動(dòng)態(tài)的!
比如說程序里面可以這樣寫:
id?helper?=?getTheReceiver(); ?
SEL?request?=?getTheSelector(); ?
[helper?performSelector:request];?
它的實(shí)現(xiàn)是基于ObjC runtime.?NSObject類實(shí)現(xiàn)了這套機(jī)制,所以每一個(gè)繼承于NSObject的類都能自動(dòng)獲得runtime的支持??。在這樣的一個(gè)類中,有一個(gè)isa指針,指向該類定義的數(shù)據(jù)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體是由編譯器編譯時(shí)為類(須繼承于NSObject)創(chuàng)建的.在這個(gè)結(jié)構(gòu)體中有包括了指向其父類類定義的指針以及Dispatch table. Dispatch table是一張SEL和IMP的對(duì)應(yīng)表??。
對(duì)于名稱相同的方法,他們都有相同的SEL,方法的名稱不包括類名稱,所以子類和父類中的同名方法擁有相同的SEL,但是他們的實(shí)現(xiàn)可以各不相同,因而在他們各自的Dispatch表中SEL所對(duì)應(yīng)的IMP是不同的,IMP是一個(gè)函數(shù)指針,而雖然每一個(gè)SEL對(duì)應(yīng)的是一個(gè)方法的名稱,但考慮到效率,SEL本身是一個(gè)整型,編譯器會(huì)另外生成一張SEL和方法名稱對(duì)應(yīng)的表??。有了這樣的結(jié)構(gòu),objc就可以實(shí)現(xiàn)多態(tài)了??。還是這行代碼:
[theClass?foo:10];?
是向theClass發(fā)送了foo:消息,那么首先在theClass的類結(jié)構(gòu)的Dispatch table里找有沒有對(duì)應(yīng)的SEL,如果有的話,就表示theClass有響應(yīng)該消息的方法,程序就跳到該方法的代碼地址頭(由IMP指定),開始執(zhí)行??。如果在theClass的Dispatch table找不到對(duì)應(yīng)的SEL,那么就會(huì)通過isa所指的結(jié)構(gòu)體中包含的父類指針,到父類里面去尋找,如果到最后還是沒有找到,就會(huì)出現(xiàn)runtime error.所以說,即使theClass以及它的父類都沒有定義-(void) foo:(int)a方法,程序還是可以通過編譯,但如果是用xcode的話,編譯器會(huì)有警告,告知theClass可能無法響應(yīng)該消息??。不會(huì)報(bào)錯(cuò)的原因是類的方法也可以在執(zhí)行時(shí)刻創(chuàng)建!上面的代碼:
class_addMethod([MyClass?class],?@selector(dynGeneratedMethod:),(IMP)myClassIMP,”v@:i”);?
就是給MyClass類在執(zhí)行時(shí)刻增加了一個(gè)響應(yīng)dynGeneratedMethod:消息的方法,這樣之后對(duì)任何MyClass的instance類發(fā)送dynGeneratedMethod:消息,就會(huì)得到響應(yīng)了.myClassIMP是類收到該消息時(shí)要調(diào)用的方法,其聲明如下:
void?myClassIMP(id?_rec,?SEL?_cmd,?int?theInt)?
這個(gè)方法的前面兩個(gè)參數(shù)是必須的,之后的參數(shù)才是我們實(shí)際用到的參數(shù),數(shù)目和@selector()中的冒號(hào)數(shù)一樣,冒號(hào)數(shù)代表的就是參數(shù)個(gè)數(shù)??。第一個(gè)參數(shù)是消息的接受對(duì)象,是MyClass的實(shí)例,第二個(gè)參數(shù)是由SEL代表的具體消息??。
Class_addMethod的最后一個(gè)參數(shù)是表示dynGeneratedMethod:的返回值和參數(shù)信息,不過我自己試了一下,這個(gè)參數(shù)不起作用??。
幾個(gè)要點(diǎn):
1、對(duì)于C中被稱為函數(shù)(function)和函數(shù)調(diào)用(function call)的地方,在ObjC中被叫做方法(method)和發(fā)送消息(send message).試圖調(diào)用未定義的方法會(huì)導(dǎo)致編譯錯(cuò)誤,而發(fā)送一條消息,即使沒有任何類定義了響應(yīng)該消息的方法,編譯時(shí)也不會(huì)報(bào)錯(cuò),從語義上講這也是對(duì)的,發(fā)一條消息本來就不要求一定有人會(huì)響應(yīng),不過如果執(zhí)行到發(fā)送消息的代碼時(shí)真的沒有類可以響應(yīng)的話,是會(huì)發(fā)生runtime error,為了避免這種事情發(fā)生,可以先進(jìn)行檢測,這樣寫:
if(?[myClass?respondsToSelector:@selector(foo:)]) ?
{ ?
???[myClass?foo:10]; ?
}?
我感覺ObjC這樣的一套sender receiver的定義更注重面向?qū)ο蟮母拍??。類是一個(gè)接收者(receiver),如果定義了某個(gè)方法,就可以接收和這個(gè)方法名稱相同的消息??。而使用該類的client(sender),則嘗試向該類發(fā)送消息.如果匹配了,就跳到類的方法里執(zhí)行??。
2、方法名稱是諸如foo:,不包括返回類型,參數(shù)類型,而又因?yàn)橐粋€(gè)foo:對(duì)應(yīng)于一個(gè)SEL,所以說ObjC不支持相同的foo:有不同的返回類型,也不支持重載??。不過類方法和實(shí)例方法可以有相同的名字,而又有不同類型的參數(shù)和返回類型,因?yàn)樗鼈儾皇翘幵谕粡坉ispatch table中??。
3、不僅類的方法可以運(yùn)行時(shí)刻創(chuàng)建,類本身也可以在運(yùn)行時(shí)刻創(chuàng)建,前面提到繼承于NSObject的類,編譯器會(huì)幫忙生成ObjC runtime所需要的類結(jié)構(gòu)定義,只要我們?cè)诖a里也按照那個(gè)結(jié)構(gòu)創(chuàng)建了自己的類,那一樣可以獲得ObjC runtime的支持??。?
?
轉(zhuǎn)載于:https://www.cnblogs.com/DamonTang/archive/2012/11/08/2760580.html
總結(jié)
以上是生活随笔為你收集整理的详解Objective-C消息传递机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows下安装pip
- 下一篇: 注入技术--LSP劫持注入