Runtime系列(淺析數(shù)據(jù)結(jié)構(gòu))
上篇文章說到類與元類,我們已經(jīng)知道類的本質(zhì)是結(jié)構(gòu)體objc_class,接下來看看objc_class是什么
isa指向元類,super_class表示當(dāng)前類的父類,這兩個(gè)成員我們已經(jīng)很熟悉,這里不再贅述(可參考 類與元類 、 引文)。
name:類名
version:版本相關(guān)信息,默認(rèn)為0
info:提供運(yùn)行期使用的標(biāo)示符
instance_size:當(dāng)前類實(shí)例變量的大?。òǜ割悾?/p>
- ivars
從objc_class可以看到,ivars是結(jié)構(gòu)體objc_ivar_list的指針
結(jié)構(gòu)體各成員見名知意,不再逐個(gè)解釋??梢?,ivars其實(shí)是一個(gè)存儲(chǔ)類中成員變量相關(guān)信息的鏈表。
其中
- methodLists
從objc_class可以看到,methodLists是結(jié)構(gòu)體objc_method_list的二級指針
又見結(jié)構(gòu)體的自嵌套,可見methodLists也是鏈表,存儲(chǔ)類中的方法相關(guān)信息。由于是二級指針,所以可以動(dòng)態(tài)修改類中的方法,這也是分類的實(shí)現(xiàn)原理。
其中
這里要解釋一下SEL和IMP:
- SEL
- 什么是SEL
SEL是對方法的包裝,常見的定義有SEL sel1 = @selector(message1); SEL sel2 = NSSelectorFromString(message2);
- 為什么要對方法進(jìn)行包裝
獲取方法所對應(yīng)的ID - 什么是方法對應(yīng)的ID
可以理解為方法名的一種映射
- 什么是SEL
來看下面的例子
- (void)helloWorld:(int)flag; - (void)helloWorld:(float)flag;
在OC中,這樣寫會(huì)報(bào)錯(cuò),錯(cuò)誤類型為重復(fù)聲明。如果這樣寫:
- (int)helloWorld:(int)flag; - (float)helloWorld:(float)flag;
即使返回值不同,仍然是重復(fù)聲明。因?yàn)樗麄兊姆椒嗤?,都是helloWorld:,所以這四個(gè)方法對應(yīng)著同一個(gè)SEL。
不過這是在同一個(gè)類中,如果是不同的類呢?
無論是在同一個(gè)類還是在不同的類,只要方法名相同,SEL就相同,獲取的ID就相同。
既然方法名相同ID就相同,如果兩個(gè)非繼承關(guān)系的類存在相同方法名的方法,那該如何確定執(zhí)行那個(gè)類中的方法?
再來回顧引文提到的函數(shù)
id objc_msgSend(id self, SEL op, ...)
[receiver message]還有個(gè)接受者,即使ID相同,不同的接收者定位到的方法仍然不同,而各類中不允許存在相同方法名的方法,這樣就確定了***性。
- IMP
相比于SEL,IMP要爽快得多。IMP的本質(zhì)是函數(shù)指針,直接通過IMP就可以找到各個(gè)方法。這樣效率更高,因?yàn)槔@過了消息傳遞階段,直接定位。
回到objc_class。
cache和protocols不再深入,這里只做簡單介紹
- cache
cache同樣是鏈表,存儲(chǔ)曾經(jīng)調(diào)用過的方法的相關(guān)信息,這樣將常用方法存到cache中,可以提高方法的查找效率。 - protocols
protocols仍然是鏈表,存儲(chǔ)當(dāng)前類(包括父類)遵守的協(xié)議的相關(guān)信息。