博客地址:張飛的技術(shù)博客
在學(xué)習(xí)Runtime的過(guò)程中,有不少的知識(shí)點(diǎn)是平時(shí)難以接觸到的咕娄。所以剛開(kāi)始接觸Runtime的時(shí)候會(huì)碰到好些不熟悉的小知識(shí)點(diǎn)昌执。下面就隨我扒一扒Runtime中那些需要知道的小知識(shí)點(diǎn)吧摄杂!
SEL
SEL
是一種類型精置,該類型表示對(duì)方法的一種封裝计寇,平時(shí)我們說(shuō)的選擇器selector就是它啦!Objective-C在編譯時(shí)候呢脂倦,會(huì)依據(jù)每一個(gè)方法的名字、參數(shù)序列元莫,生成一個(gè)唯一的整型標(biāo)識(shí)(Int類型的地址)赖阻,這個(gè)標(biāo)識(shí)就是SEL。也就是說(shuō)每一個(gè)SEL對(duì)應(yīng)一個(gè)方法踱蠢,根據(jù)一個(gè)SEL就能找到一個(gè)方法火欧,然后對(duì)它進(jìn)行調(diào)用等操作。其實(shí)我們也可以理解為SEL是一個(gè)方法的指針(實(shí)際上不是)茎截。來(lái)看看它的真面目吧,在objc.h
的49行處可以發(fā)現(xiàn):
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
這樣看來(lái)SEL的本質(zhì)是一個(gè)objc_selector
類型的結(jié)構(gòu)體指針苇侵。那該如何生成一個(gè)SEL類型的數(shù)據(jù)呢?編譯器提供的有選擇器@selector,NSSelectorFromString()方法企锌,sel_registerName函數(shù)榆浓。
SEL methodSel1 = @selector(method);
SEL methodSel2 = NSSelectorFromString(method);
```
>NOTE:只要方法名一樣,那么這個(gè)方法的SEL就一樣撕攒,不管同名的方法是有沒(méi)有關(guān)系(繼承),即使在不同的類方法名一樣那么這個(gè)方法的SEL也是一樣的陡鹃。但是同一個(gè)類中不能存在同名方法(即使參數(shù)類型一樣也不行,當(dāng)然參數(shù)個(gè)數(shù)不一樣可以)抖坪,在不同的類里面是可以有同名方法的萍鲸,不同類的實(shí)例對(duì)象執(zhí)行相同的selector時(shí),會(huì)在各自的方法列表中去根據(jù)selector去尋找自己對(duì)應(yīng)的IMP擦俐。
本質(zhì)上脊阴,SEL只是一個(gè)指向方法的指針(準(zhǔn)確的說(shuō),只是一個(gè)根據(jù)方法名hash化了的KEY值蚯瞧,能唯一代表一個(gè)方法)嘿期,它的存在只是為了加快方法的查詢速度。
###IMP
上面講了状知,根據(jù)SEL能尋找到函數(shù)秽五,其實(shí)IMP才是真正指向函數(shù)的指針,它指向了函數(shù)實(shí)現(xiàn)的首地址饥悴。其實(shí)SEL就是為了查找方法的IMP的坦喘。取得IMP后盲再,我們就獲得了執(zhí)行這個(gè)方法代碼的入口點(diǎn),此時(shí)瓣铣,我們就可以像調(diào)用普通的C語(yǔ)言函數(shù)一樣來(lái)使用這個(gè)函數(shù)指針了答朋。
>NOTE:通過(guò)取得IMP,我們可以跳過(guò)Runtime的消息傳遞機(jī)制棠笑,直接執(zhí)行IMP指向的函數(shù)實(shí)現(xiàn)梦碗,這樣省去了Runtime消息傳遞過(guò)程中所做的一系列查找操作,會(huì)比直接向?qū)ο蟀l(fā)送消息高效一些蓖救。
###Method
Method是一個(gè)結(jié)構(gòu)體洪规,用于表示類中定義的方法。實(shí)際上相當(dāng)于在SEL和IMP之間作了一個(gè)映射循捺。有了SEL斩例,我們便可以找到對(duì)應(yīng)的IMP,從而調(diào)用方法的實(shí)現(xiàn)代碼从橘。
>方法操作相關(guān)函數(shù):
``` Objective-C
// 調(diào)用指定方法的實(shí)現(xiàn)
id method_invoke ( id receiver, Method m, ... );
// 調(diào)用返回一個(gè)數(shù)據(jù)結(jié)構(gòu)的方法的實(shí)現(xiàn)
void method_invoke_stret ( id receiver, Method m, ... );
// 獲取方法名
SEL method_getName ( Method m );
// 返回方法的實(shí)現(xiàn)
IMP method_getImplementation ( Method m );
// 獲取描述方法參數(shù)和返回值類型的字符串
const char * method_getTypeEncoding ( Method m );
// 獲取方法的返回值類型的字符串
char * method_copyReturnType ( Method m );
// 獲取方法的指定位置參數(shù)的類型字符串
char * method_copyArgumentType ( Method m, unsigned int index );
// 通過(guò)引用返回方法的返回值類型字符串
void method_getReturnType ( Method m, char *dst, size_t dst_len );
// 返回方法的參數(shù)的個(gè)數(shù)
unsigned int method_getNumberOfArguments ( Method m );
// 通過(guò)引用返回方法指定位置參數(shù)的類型字符串
void method_getArgumentType ( Method m, unsigned int index, char *dst, size_t dst_len );
// 返回指定方法的方法描述結(jié)構(gòu)體
struct objc_method_description * method_getDescription ( Method m );
// 設(shè)置方法的實(shí)現(xiàn)
IMP method_setImplementation ( Method m, IMP imp );
// 交換兩個(gè)方法的實(shí)現(xiàn)
void method_exchangeImplementations ( Method m1, Method m2 );
```
上面這些函數(shù)在通過(guò)Runtime實(shí)現(xiàn)一些東西的時(shí)候或者在看別人的代碼的時(shí)候或許能用到念赶,只需要知道有這么一些函數(shù)就可以了。
###Class
Class在我的一篇文章中有詳細(xì)的講解恰力,請(qǐng)移步[OC中的類是怎么來(lái)的叉谜?](),這里不在贅訴了。
###_cmd
通過(guò)_cmd這個(gè)關(guān)鍵字能獲取當(dāng)前方法的`SEL`踩萎⊥>郑看個(gè)栗子吧:
```
- (void)testCmd {
SEL currentMethodSel = _cmd;
NSLog(@"currentSel is :%s",(char *)currentMethodSel);
}
```
上面的打印結(jié)果為:
```
currentSel is :testCmd
```
###objc_msgSend
在OC中,對(duì)方法的調(diào)用被稱之為發(fā)送消息驻民。其實(shí)我們對(duì)方法的調(diào)用最后都會(huì)被編譯為下面的這種形式:
>對(duì)于函數(shù):
```
[target method1];
[target method2:var];
```
>沒(méi)有參數(shù):
```
objc_msgSend(target,@selector(method1));
```
>有參數(shù):
```
objc_msgSend(target,@selector(method2:),var);
```
方法的調(diào)用過(guò)程在我的上一篇文章[OC中的類是怎么來(lái)的翻具?]()
###結(jié)尾
有人說(shuō)Runtime是OC的本質(zhì)和精華,雖然我們?cè)陧?xiàng)目中不經(jīng)常用回还,但是理解還是有很大的用處的裆泳。喜歡我的文章就給我鼓勵(lì)吧!
![](http://upload-images.jianshu.io/upload_images/465386-2f3e022157448deb.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/200)