上篇文章說(shuō)到類與元類,我們已經(jīng)知道類的本質(zhì)是結(jié)構(gòu)體objc_class
,接下來(lái)看看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í)例變量的大小(包括父類)
- ivars
從objc_class
可以看到,ivars
是結(jié)構(gòu)體objc_ivar_list
的指針
結(jié)構(gòu)體各成員見(jiàn)名知意,不再逐個(gè)解釋。可見(jiàn),ivars
其實(shí)是一個(gè)存儲(chǔ)類中成員變量相關(guān)信息的鏈表。
其中
- methodLists
從objc_class
可以看到,methodLists
是結(jié)構(gòu)體objc_method_list
的二級(jí)指針
又見(jiàn)結(jié)構(gòu)體的自嵌套,可見(jiàn)methodLists
也是鏈表,存儲(chǔ)類中的方法相關(guān)信息。由于是二級(jí)指針,所以可以動(dòng)態(tài)修改類中的方法,這也是分類的實(shí)現(xiàn)原理。
其中
這里要解釋一下SEL
和IMP
:
- SEL
- 什么是SEL
SEL是對(duì)方法的包裝,常見(jiàn)的定義有
SEL sel1 = @selector(message1);
SEL sel2 = NSSelectorFromString(message2);
- 為什么要對(duì)方法進(jìn)行包裝
獲取方法所對(duì)應(yīng)的ID
- 什么是方法對(duì)應(yīng)的
ID
可以理解為方法名的一種映射
來(lái)看下面的例子
- (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)樗麄兊姆椒嗤际?code>helloWorld:,所以這四個(gè)方法對(duì)應(yīng)著同一個(gè)SEL
。
不過(guò)這是在同一個(gè)類中,如果是不同的類呢?
無(wú)論是在同一個(gè)類還是在不同的類,只要方法名相同,SEL就相同,獲取的ID就相同。
既然方法名相同ID
就相同,如果兩個(gè)非繼承關(guān)系的類存在相同方法名的方法,那該如何確定執(zhí)行那個(gè)類中的方法?
再來(lái)回顧引文提到的函數(shù)
id objc_msgSend(id self, SEL op, ...)
[receiver message]
還有個(gè)接受者,即使ID
相同,不同的接收者定位到的方法仍然不同,而各類中不允許存在相同方法名的方法,這樣就確定了唯一性。
- IMP
相比于SEL
,IMP
要爽快得多。IMP
的本質(zhì)是函數(shù)指針,直接通過(guò)IMP
就可以找到各個(gè)方法。這樣效率更高,因?yàn)槔@過(guò)了消息傳遞階段,直接定位。
回到objc_class
。
cache
和protocols
不再深入,這里只做簡(jiǎn)單介紹
- cache
cache
同樣是鏈表,存儲(chǔ)曾經(jīng)調(diào)用過(guò)的方法的相關(guān)信息,這樣將常用方法存到cache中,可以提高方法的查找效率。 - protocols
protocols
仍然是鏈表,存儲(chǔ)當(dāng)前類(包括父類)遵守的協(xié)議的相關(guān)信息。