SEL selector (二)
SEL消息機制工作原理是什么?
引用下面文章:
?
我們在之前有提到,一個類就像一個 C 結(jié)構(gòu).NSObject 聲明了一個成員變量: isa. 由于 NSObject 是所有類的根類,所以所有的對象都會有一個 isa 的成員變量[公共繼承].而該 isa 變量指向該對象的類(圖3.15)[類在Objective-C中也是一個實體, 由于存在Objective-C 運行環(huán)境所有的類將有自己的存儲空間.Objective-C 運行環(huán)境將為每個類分配空間. 這里 所說的 isa,正是指向這樣一個類的空間. 從而建立類和對象之間的對應關(guān)系.] 類空間 包含了該類定義的成員變量,以及方法實現(xiàn), 還包含了指向自己父類空間的指針.?
?
?
方法以 selector 作為索引. selector 的數(shù)據(jù)類型是 SEL. 雖然 SEL 定義成 char*, 我們可 以把它想象成 int. 每個方法的名字對應一個唯一的 int 值.比如, 方法 addObject: 可能 對應的是 12. 當尋找該方法是, 使用的是 selector,而不是名字 @"addObject:"
Objective-C 數(shù)據(jù)結(jié)構(gòu)中,存在一個 name - selector 的映射表如圖 3.16?
?
?
在編譯的時候, 只要有方法的調(diào)用, 編譯器都會通過 selector 來查找,所以 (假設 addObject 的 selector 為 12)
[myObject addObject:yourObject];將會編譯變成
objc_msgSend(myObject, 12, yourObject);
這里,objec_msgSend()函數(shù)將會使用 myObjec 的 isa 指針來找到 myObject 的類空間結(jié)構(gòu)并 在類空間結(jié)構(gòu)中查找 selector 12 所對應的方法.如果沒有找到,那么將使用指向父類的指 針找到父類空間結(jié)構(gòu)進行 selector 12 的查找. 如果仍然沒有找到,就繼續(xù)往父類的父類一 直找,直到找到為止, 如果到了根類 NSObject 中仍然找不到,將會拋出異常.
我們可以看到, 這是一個很動態(tài)的查找過程.類的結(jié)構(gòu)可以在運行的時候改變,這樣可以很 容易來進行功能擴展[Objective-C 語言是動態(tài)語言, 支持動態(tài)綁定.
?
再來看
Objective-C如何獲取消息的原理詳解文章
Objective-C獲取消息工作原理是本文要介紹的內(nèi)容,看name mangling的時候,也講到了Objective-C的name mangling,于是又重新讀了一下Objective-C?2.0 programming Language以及Objective-C?2.0 Runtime Reference里的相關(guān)內(nèi)容,自己歸納一下。
先貼一段代碼:
?
[plain]?view plaincopy這段代碼執(zhí)行的結(jié)果是在控制臺上輸出:
?
dynamic added method:10
?
接著來詳細分析一下上面的代碼:
在ObjC的類中這樣的一個聲明 – (void)foo:(int)a;被稱作方法(method),而在調(diào)用的地方: [theClass foo:10];則被稱之為發(fā)送消息(send message),具體來說是給對象theClass 發(fā)送foo:消息,注意這里foo后面的”:”,它也是消息名稱的一部分,最前面的'-'代表實例方法,'+'代表類方法。而類似的語句,在C或C++ 中,通常被稱為呼叫函數(shù)(call function),在ObjC中,函數(shù)(function)一詞很少用到,不是它不存在,而是它被ObjC runtime給隱藏了起來。
如前所述,ObjC是以消息機制來工作的,但其實諸如-(void)foo:(int)a的語句在編譯時被 objc_msgSend(receiver,selector,arg1,arg2,….)替換了,所以其實每一條發(fā)送消息的代碼本質(zhì)上還是調(diào)用函數(shù) (call function),不過他們調(diào)用的都是同一個函數(shù)objc_msgSend(也可能是objc_msgSend_stret(返回值是結(jié)構(gòu) 體),objc_msgSend_fpret(返回值是浮點型)等)
分析objc_msgSend的參數(shù),第一個receiver的類型是id,代表接受消息的對象,第二個是selector代表接收對象的方法,后面的是該方法的參數(shù),之前那條語句的被編譯器替換后就是:
[theClass foo:10]? -> objc_msg(theClass,@selector(foo:),10);?
?
因為消息的接受對象和接受對象的方法都參數(shù)化,所以在運行時刻,接受對象和接受對象的方法都可以是動態(tài)的!
比如說程序里面可以這樣寫:
id helper = getTheReceiver();? SEL request = getTheSelector();? [helper performSelector:request];它的實現(xiàn)是基于ObjC runtime. NSObject類實現(xiàn)了這套機制,所以每一個繼承于NSObject的類都能自動獲得runtime的支持。在這樣的一個類中,有一個isa指針,指向 該類定義的數(shù)據(jù)結(jié)構(gòu)體,這個結(jié)構(gòu)體是由編譯器編譯時為類(須繼承于NSObject)創(chuàng)建的.在這個結(jié)構(gòu)體中有包括了指向其父類類定義的指針以及 Dispatch table. Dispatch table是一張SEL和IMP的對應表。
對于名稱相同的方法,他們都有相同的SEL,方法的名稱不包括類名稱,所以子類和父類中的同名方法擁有相同的SEL,但是他們的實現(xiàn)可以各不相同, 因而在他們各自的Dispatch表中SEL所對應的IMP是不同的,IMP是一個函數(shù)指針,而雖然每一個SEL對應的是一個方法的名稱,但考慮到效 率,SEL本身是一個整型,編譯器會另外生成一張SEL和方法名稱對應的表。有了這樣的結(jié)構(gòu),objc就可以實現(xiàn)多態(tài)了。還是這行代碼:
[theClass foo:10];
是向theClass發(fā)送了foo:消息,那么首先在theClass的類結(jié)構(gòu)的Dispatch table里找有沒有對應的SEL,如果有的話,就表示theClass有響應該消息的方法,程序就跳到該方法的代碼地址頭(由IMP指定),開始執(zhí)行。 如果在theClass的Dispatch table找不到對應的SEL,那么就會通過isa所指的結(jié)構(gòu)體中包含的父類指針,到父類里面去尋找,如果到最后還是沒有找到,就會出現(xiàn)runtime error.所以說,即使theClass以及它的父類都沒有定義-(void) foo:(int)a方法,程序還是可以通過編譯,但如果是用xcode的話,編譯器會有警告,告知theClass可能無法響應該消息。不會報錯的原因 是類的方法也可以在執(zhí)行時刻創(chuàng)建!上面的代碼:
class_addMethod([MyClass class], @selector(dynGeneratedMethod:),(IMP)myClassIMP,”v@:i”);
就是給MyClass類在執(zhí)行時刻增加了一個響應dynGeneratedMethod:消息的方法,這樣之后對任何MyClass的instance類 發(fā)送dynGeneratedMethod:消息,就會得到響應了.myClassIMP是類收到該消息時要調(diào)用的方法,其聲明如下:
void myClassIMP(id _rec, SEL _cmd, int theInt)
?
這個方法的前面兩個參數(shù)是必須的,之后的參數(shù)才是我們實際用到的參數(shù),數(shù)目和@selector()中的冒號數(shù)一樣,冒號數(shù)代表的就是參數(shù)個數(shù)。第一個參數(shù)是消息的接受對象,是MyClass的實例,第二個參數(shù)是由SEL代表的具體消息。
Class_addMethod的最后一個參數(shù)是表示dynGeneratedMethod:的返回值和參數(shù)信息,不過我自己試了一下,這個參數(shù)不起作用。
幾個要點:
1、對于C中被稱為函數(shù)(function)和函數(shù)調(diào)用(function call)的地方,在ObjC中被叫做方法(method)和發(fā)送消息(send message).試圖調(diào)用未定義的方法會導致編譯錯誤,而發(fā)送一條消息,即使沒有任何類定義了響應該消息的方法,編譯時也不會報錯,從語義上講這也是對 的,發(fā)一條消息本來就不要求一定有人會響應,不過如果執(zhí)行到發(fā)送消息的代碼時真的沒有類可以響應的話,是會發(fā)生runtime error,為了避免這種事情發(fā)生,可以先進行檢測,這樣寫
if( [myClass respondsToSelector:@selector(foo:)])? {? ???[myClass foo:10];? }我感覺ObjC這樣的一套sender receiver的定義更注重面向?qū)ο蟮母拍睢n愂且粋€接收者(receiver),如果定義了某個方法,就可以接收和這個方法名稱相同的消息。而使用該 類的client(sender),則嘗試向該類發(fā)送消息.如果匹配了,就跳到類的方法里執(zhí)行。
2、方法名稱是諸如foo:,不包括返回類型,參數(shù)類型,而又因為一個foo:對應于一個SEL,所以說ObjC不支持相同的foo:有不同的返回 類型,也不支持重載。不過類方法和實例方法可以有相同的名字,而又有不同類型的參數(shù)和返回類型,因為它們不是處在同一張dispatch table中。
3、不僅類的方法可以運行時刻創(chuàng)建,類本身也可以在運行時刻創(chuàng)建,前面提到繼承于NSObject的類,編譯器會幫忙生成ObjC runtime所需要的類結(jié)構(gòu)定義,只要我們在代碼里也按照那個結(jié)構(gòu)創(chuàng)建了自己的類,那一樣可以獲得ObjC runtime的支持。
轉(zhuǎn)載于:https://www.cnblogs.com/idLuffy/p/4036514.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的SEL selector (二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对象的属性
- 下一篇: mobile cpu上禁用alpha t