上下文- 前情概述:
上一篇文章 OC對(duì)象占用內(nèi)存原理 (一文徹底搞懂) 中我們講到 OC 對(duì)象本身是一個(gè)結(jié)構(gòu)體, 這個(gè)結(jié)構(gòu)體只有一個(gè) isa
指針, 關(guān)于 OC 對(duì)象實(shí)際開辟內(nèi)存原理我們搞清楚了. 那么本篇文章就結(jié)合 runtime
來(lái)實(shí)際探討一下 OC 對(duì)象方法查找的具體流程以及方法調(diào)用的原理.
isa
任何數(shù)據(jù)結(jié)構(gòu),只要在恰當(dāng)?shù)奈恢镁哂幸粋€(gè)指針指向一個(gè)
class
渊涝,那么眉踱,它都可以被認(rèn)為是一個(gè)對(duì)象撮珠。objc_class
和objc_object
的struct
指針都能稱為對(duì)象,我們?yōu)榱藚^(qū)分可以把它們分別叫做類對(duì)象和實(shí)例對(duì)象筹裕,其中實(shí)例對(duì)象的isa
指針指向類對(duì)象 (這個(gè)指向下面會(huì)講)。
在 OC 中,一個(gè)對(duì)象所屬于哪個(gè)類澜薄,是由它的 isa
指針指向的。這個(gè) isa
指針指向這個(gè)對(duì)象所屬的 class
摊册。
個(gè)人建議: 可以適當(dāng)結(jié)合英文意思來(lái)理解, isa -> is a what? -> 你是個(gè)啥.
實(shí)例對(duì)象 | 類對(duì)象 | 元類 的基礎(chǔ)概念區(qū)分
簡(jiǎn)單來(lái)個(gè) ??:
提前創(chuàng)建了一個(gè)類 LBPerson
繼承與 NSObject
. 然后創(chuàng)建這個(gè)對(duì)象.
LBPerson * obj = [[LBPerson alloc] init];
- 實(shí)例對(duì)象 :
obj
, 其isa
指向類對(duì)象 - 類對(duì)象 :
LBPerson
, 其isa
指向元類 - 元類 : 元類就是類對(duì)象所屬的類 , 其
isa
指向根源類, 也就是NSObject
的元類.
如果我們?cè)趧?chuàng)建一個(gè)子類,繼承與
LBPerson
, 那么這個(gè)子類就是上圖中的 Subclass(class)
, 實(shí)例化出來(lái)的對(duì)象就是 Instance Of Subclass
.
需要注意的是:
- 任何元類的
isa
都指向根元類. 根源類的isa
指向它自己. 以此來(lái)實(shí)現(xiàn)一個(gè)完整的內(nèi)循環(huán), 所有的類,都會(huì)有類型,isa
不會(huì)為空.NSObject
的父類為nil
.- 根元類的父類指向
NSObject
.
那么這三種類型的對(duì)象分別都持有什么呢? 比如說(shuō) 方法到底存在哪個(gè)對(duì)象中? 屬性存在哪個(gè)對(duì)象? 接下來(lái)我們一一探討.
類對(duì)象
類對(duì)象我們通過(guò)源碼來(lái)查看其中保存了哪些內(nèi)容,
shift
+ cmd
+ O
, 輸入 objc.
找到 38 行:
typedef struct objc_class *Class;
cmd + 點(diǎn) objc_class
, 來(lái)到類對(duì)象的結(jié)構(gòu)體中.
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
稍微介紹一下各個(gè)屬性的意思:
-
isa
:上文講述過(guò) -
super_class
:父類肤京,如果該類已經(jīng)是最頂層的根類,那么它為nil
。 -
version
:類的版本信息,默認(rèn)為 0 -
info
:供運(yùn)行期使用的一些位標(biāo)識(shí)茅特。 -
instance_size
:該類的實(shí)例變量大小 -
ivars
:成員變量的列表 -
methodLists
:方法列表 -
cache
:緩存結(jié)構(gòu)體桶 -
protocols
:協(xié)議列表
類對(duì)象總結(jié):
是一個(gè)功能完整的對(duì)象忘分。特殊之處在于它們是在運(yùn)行時(shí)由編譯器創(chuàng)建的,它沒(méi)有自己的實(shí)例變量(這里區(qū)別于類的成員變量白修,他們是屬于實(shí)例對(duì)象的妒峦,而不是屬于類對(duì)象的,類方法是屬于類對(duì)象自己的)兵睛,但 類對(duì)象中存著成員變量與實(shí)例方法列表肯骇。
存儲(chǔ)內(nèi)容如圖:
實(shí)例對(duì)象
先來(lái)看控制臺(tái)提供給我們的.
我們看到實(shí)例對(duì)象中有屬性, 和
isa
. 這也跟我們上一篇博客中探討 OC 對(duì)象占用內(nèi)存原理 得出的結(jié)果是一模一樣.
實(shí)例對(duì)象總結(jié):
當(dāng)我們?cè)诖a中新創(chuàng)建一個(gè)實(shí)例對(duì)象時(shí),拷貝了實(shí)例所屬的類的成員變量祖很,但不拷貝類定義的方法笛丙。調(diào)用實(shí)例方法時(shí),根據(jù)實(shí)例的isa
指針去尋找方法對(duì)應(yīng)的函數(shù)指針假颇。
存儲(chǔ)內(nèi)容如圖:
元類
OC 的類方法是使用元類的根本原因, 而這也是元類中存儲(chǔ)的最重要的內(nèi)容. 當(dāng)你給類發(fā)消息時(shí)胚鸯,消息是在尋找這個(gè)類的元類的方法列表. 類對(duì)象是元類的實(shí)例. 因此元類存儲(chǔ)著類方法.
存儲(chǔ)內(nèi)容如下:
元類對(duì)象總結(jié):
OC 的類方法是使用元類的根本原因,因?yàn)槠渲写鎯?chǔ)著對(duì)應(yīng)的類對(duì)象調(diào)用的方法即類方法笨鸡。其他時(shí)候都傾向于隱藏元類姜钳,因此真實(shí)世界沒(méi)有人發(fā)送消息給元類對(duì)象。元類的定義和創(chuàng)建看起來(lái)都是編譯器自動(dòng)完成的形耗,無(wú)需人為干涉傲须。
要獲取一個(gè)類的元類,可使用如下定義的函數(shù):
Class objc_getMetaClass(const char* name); //name為類的名字
此外還有一個(gè)獲取對(duì)象所屬的類的函數(shù):
Class object_getClass(id obj) ;
由于類對(duì)象是元類的實(shí)例趟脂,所以當(dāng)傳入的參數(shù)為類名時(shí)泰讽,返回的就是指向該類所屬的元類的指針。
總結(jié)
基于此, 我們可以根據(jù)其存儲(chǔ)內(nèi)容稍微總結(jié)一下
1??: 實(shí)例對(duì)象的
isa
指向類對(duì)象,當(dāng)調(diào)用對(duì)象方法已卸,通過(guò)實(shí)例對(duì)象的isa
找到類對(duì)象,最終找到對(duì)對(duì)象方法進(jìn)行調(diào)用2??: 類對(duì)象的
isa
指向元類佛玄,調(diào)用類方法,通過(guò)類對(duì)象中的isa
找到元類累澡,最終找到元類中的類方法進(jìn)行調(diào)用3??: 當(dāng)子類的對(duì)象要調(diào)用父類的對(duì)象方法梦抢,先通過(guò)子類的
isa
找到父類的class
. 然后通過(guò)superClass
找到父類的class
最后找到消息進(jìn)行調(diào)用.
最后要提醒注意的一點(diǎn)是:
根類不一定是
NSObject
,因?yàn)楹竺娼榻B的objc_allocateClassPair
函數(shù)也可以創(chuàng)建出一個(gè)根類愧哟。
元類的實(shí)際應(yīng)用
類對(duì)象和元類對(duì)象的相關(guān)方法:
- ① :
object_getClass
跟隨實(shí)例的isa
指針奥吩,返回此實(shí)例所屬的類,對(duì)于實(shí)例對(duì)象 (instance
) 返回的是類 (class
) ,對(duì)于類 (class
) 則返回的是元類 (metaclass
) .
- ② : class 方法對(duì)于實(shí)例對(duì)象 (
instance
) 會(huì)返回類 (class
) , 但對(duì)于類 (class
) 則不會(huì)返回元類 (metaclass
), 而只會(huì)返回類本身蕊梧,即[@"instance" class]
返回的是__NSCFConstantString
, 而[NSString class]
返回的是NSString
霞赫。
- ③ :
class_isMetaClass
可判斷某類是否為元類.
- ④ : 使用
objc_allocateClassPair
可在運(yùn)行時(shí)創(chuàng)建新的類與元類對(duì),使用class_addMethod
和class_addIvar
可向類中增加方法和實(shí)例變量肥矢,最后使用objc_registerClassPair
注冊(cè)后端衰,就可以使用此類了。這體現(xiàn)了 OC 作為運(yùn)行時(shí)語(yǔ)言的強(qiáng)大之一:在代碼運(yùn)行中動(dòng)態(tài)創(chuàng)建類并添加方法甘改。
至此, 三種不同的類的基礎(chǔ)我們已經(jīng)講完, 下一篇會(huì)基于此基礎(chǔ), 探索方法查找流程.