其實(shí)我也不知道怎么開(kāi)頭, 那么就直接進(jìn)入正題吧, runtime是Objective-C動(dòng)態(tài)語(yǔ)言特性的體現(xiàn), 在實(shí)際編碼的過(guò)程中我們運(yùn)用最多的就是category中動(dòng)態(tài)創(chuàng)建屬性, Method Swizzling, 下面我們從類(lèi)和對(duì)象開(kāi)始一步一步的了解runtime。

上面這段話出自O(shè)bjective-C Runtime Programming Guide, 對(duì)于runtime, 有兩個(gè)版本, 一個(gè)叫做“modern”, 一個(gè)叫做“l(fā)egacy”?!癿odern”版本是以O(shè)C 2.0為基礎(chǔ),包括許多新的runtime特性;而“l(fā)egacy”版本則是涉及OC 1為基礎(chǔ), 我們可以不必理會(huì)。
類(lèi)和對(duì)象的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)
Class 類(lèi)

首先, 我們來(lái)分析一下Class的結(jié)構(gòu)體定義
1.isa: 在OC中, 類(lèi)也是對(duì)象, 而既然是對(duì)象, 則該對(duì)象的isa指針指向的地址則是metaClass(元類(lèi)), 對(duì)于metaClass, 下面會(huì)有圖來(lái)介紹, 這里理解類(lèi)是對(duì)象。
2.super_class:指向該類(lèi)的父類(lèi), 當(dāng)該類(lèi)已經(jīng)是NSObject等最頂層的類(lèi), 那么該類(lèi)的super_class為NULL。
3.name:類(lèi)名,及該類(lèi)的名稱(chēng), 如創(chuàng)建了一個(gè)叫MyClass類(lèi)繼承NSObject, 則該類(lèi)的name為MyClass。
4.instance_size:該類(lèi)實(shí)例變量的大小, 可以通過(guò)調(diào)用class_getInstanceSize(Class cls)方法來(lái)得到其值。
5.ivars:實(shí)例變量列表, 該列表以鏈表的形式返回,通過(guò)節(jié)點(diǎn)找到對(duì)應(yīng)的實(shí)例變量所在的位置。
6.methodLists:方法鏈表, 包括類(lèi)方法和對(duì)象方法。
7.cache:顧名思義, cache是緩存的意思, 但是此處的cache是對(duì)方法的緩存, 當(dāng)接收者接收到一個(gè)消息是, 首先會(huì)根據(jù)isa指針去尋找能夠響應(yīng)該方法的對(duì)象, 當(dāng)調(diào)用該方法后, 調(diào)用的方法會(huì)緩存到cache中, 下次調(diào)用的時(shí)候會(huì)先從cache中查找該方法, 如果該方法不存在, 再去methodLists中查找方法, 這樣大大的提高的程序運(yùn)行的效率。
例如: UILabel *label = [[UILabel alloc] init];
1.創(chuàng)建label對(duì)象, 首先調(diào)用+alloc的方法, 因?yàn)閁ILabel沒(méi)有+alloc方法, 所以去父類(lèi)UIView中找,沒(méi)有再到UIResponder, 最后在NSObject中找到了+alloc方法, 之后會(huì)給UILabel分配內(nèi)存空間, 同時(shí)+alloc方法存到cache中。
2.之后同樣的方法執(zhí)行init方法, 同樣,-init方法被存到cache中。
3.如果再通過(guò)該方式創(chuàng)建UILabel對(duì)象, 則會(huì)先去cache中查找方法, 不必一直去method_lists中查找。
objc_object與id

對(duì)于對(duì)象來(lái)說(shuō), 其結(jié)構(gòu)體中只有一個(gè)isa指針, 指向其類(lèi), 當(dāng)runtime運(yùn)行objc_msgSend方法的時(shí)候, 去方法列表中尋找能夠響應(yīng)的方法, 運(yùn)行方法。
當(dāng)創(chuàng)建一個(gè)特定類(lèi)的實(shí)例對(duì)象時(shí),分配的內(nèi)存包含一個(gè)objc_object數(shù)據(jù)結(jié)構(gòu),然后是類(lèi)的實(shí)例變量的數(shù)據(jù)。NSObject類(lèi)的alloc和allocWithZone:方法使用函數(shù)class_createInstance來(lái)創(chuàng)建objc_object數(shù)據(jù)結(jié)構(gòu)。
對(duì)于我們常見(jiàn)的id, 它是一個(gè)objc_object結(jié)構(gòu)類(lèi)型的指針, id類(lèi)型可以轉(zhuǎn)化成任意類(lèi)型, 類(lèi)似于C中的void *指針的作用。
元類(lèi)(Meta Class)

想必了解runtime的人對(duì)于這張圖應(yīng)該不陌生了, 這張圖很好的解釋了meta class。
我們先看Subclass的部分, instance of Subclass, 初始化實(shí)例對(duì)象, 實(shí)例對(duì)象及為objc_object結(jié)構(gòu)體, 實(shí)例對(duì)象的isa指針會(huì)指向類(lèi), 而上文所介紹的Class結(jié)構(gòu)體中也存在isa指針, 而Class中的isa指針則會(huì)指向meta class, 同理, Superclass和Root class的isa指針與Subclass的isa指針類(lèi)似, 因?yàn)樵?lèi)也是類(lèi), 所以元類(lèi)也存在isa指針, 這是, Subclass和Superclass的isa指針都會(huì)指向Root class的meta class, 當(dāng)然這種循環(huán)不能一直下去, 所以Root class的meta class的isa指針會(huì)指向自己, 例如所有繼承NSObject的類(lèi)的meta class的isa指針都會(huì)指向NSObject的meta class, 而NSObject的meta class的isa指針則會(huì)指向自己。值得注意的是, 不管是Subclass還是Superclass, 他們的meta class都是不同的, 即每個(gè)類(lèi)的meta class都不相同。這段可能會(huì)比較繞, 天資愚鈍, 這張圖我也是看了好幾十遍才弄懂的, 希望大家能夠多想。
下面引用別人寫(xiě)好的一個(gè)例子來(lái)說(shuō)明一下,代碼中有詳細(xì)的標(biāo)注, 我們可以看到打印的結(jié)果, 對(duì)著結(jié)果, 結(jié)合上圖, 會(huì)有更好的理解


類(lèi)和對(duì)象的操作函數(shù)
這里我們介紹幾個(gè)常用的類(lèi)和對(duì)象的操作函數(shù), 如下圖:

1.對(duì)于class_getName, 傳入對(duì)應(yīng)的Class, 則會(huì)返回其名稱(chēng)
2.class_getSuperclass(Class cls), 傳入對(duì)應(yīng)的Class, 獲取其父類(lèi)
3.class_getInstanceSize(Class cls), 傳入對(duì)應(yīng)的Class, 計(jì)算出實(shí)例的大小
4.class_isMetaClass(Clas cls), 傳入對(duì)應(yīng)的Class, 返回的是BOOL類(lèi)型, 岸段該Class是否為元類(lèi)
5.class_getInstanceVariable(Class cls, const char *name),獲取到指定名稱(chēng)實(shí)例成員變量的信息, name為傳入的變量名稱(chēng)
6.class_getClassVariable(Class cls, const char *name), 獲取類(lèi)成員變量信息
7.class_copyIvarList(Class cls, unsigned int *outCount), 獲取整個(gè)成員變量列表
8.objc_getProperty(Class cls, const char *name), 獲取指定的屬性
9.objc_copyPropertyList(Class cls, unsigned int *outCount), 獲取屬性列表
注意:這里outCount參數(shù)返回的是個(gè)數(shù), 在獲取玩IvarList和PropertyList以及我們沒(méi)有提到的MethodList后要使用free()方法來(lái)釋放。具體的對(duì)于類(lèi)和對(duì)象操作的函數(shù)我會(huì)在代碼中給出, 大家可以對(duì)照代碼, 動(dòng)手操一番
動(dòng)態(tài)創(chuàng)建類(lèi)和對(duì)象
1.動(dòng)態(tài)創(chuàng)建類(lèi)

(1).objc_allocateClassPair函數(shù):如果我們要?jiǎng)?chuàng)建一個(gè)根類(lèi),則superclass指定為Nil。extraBytes通常指定為0,該參數(shù)是分配給類(lèi)和元類(lèi)對(duì)象尾部的索引ivars的字節(jié)數(shù)。
為了創(chuàng)建一個(gè)新類(lèi),我們需要調(diào)用objc_allocateClassPair。然后使用諸如class_addMethod,class_addIvar等函數(shù)來(lái)為新創(chuàng)建的類(lèi)添加方法、實(shí)例變量和屬性等。完成這些后,我們需要調(diào)用objc_registerClassPair函數(shù)來(lái)注冊(cè)類(lèi),之后這個(gè)新類(lèi)就可以在程序中使用了。
實(shí)例方法和實(shí)例變量應(yīng)該添加到類(lèi)自身上,而類(lèi)方法應(yīng)該添加到類(lèi)的元類(lèi)上。
(2).objc_disposeClassPair函數(shù)用于銷(xiāo)毀一個(gè)類(lèi),不過(guò)需要注意的是,如果程序運(yùn)行中還存在類(lèi)或其子類(lèi)的實(shí)例,則不能調(diào)用針對(duì)類(lèi)調(diào)用該方法, 對(duì)于我們需要銷(xiāo)毀該類(lèi)或者子類(lèi), 我們需要調(diào)用object_dispose(id obj)來(lái)銷(xiāo)毀已經(jīng)存在的對(duì)象, 注意的是object_dispose(id obj)函數(shù)需要在MRC環(huán)境下調(diào)用, 之后在調(diào)用objc_disposeClassPair函數(shù)銷(xiāo)毀已經(jīng)常見(jiàn)的類(lèi)。

<1>我們用objc_allocateClassPair動(dòng)態(tài)創(chuàng)建了一個(gè)類(lèi),繼承自MyClass,MyClass是我之前創(chuàng)建好的類(lèi)
<2>動(dòng)態(tài)添加叫做subMethod1的方法, 而該方法的IMP指針指向函數(shù)imp_submethod1的實(shí)現(xiàn)
<3>動(dòng)態(tài)添加實(shí)例變量, 叫做_ivar1的NSString類(lèi)型的實(shí)例變量
<4>動(dòng)態(tài)添加叫做Property2的屬性
<5>調(diào)用objc_registerClassPair注冊(cè)該類(lèi), 需要注意的是, 我們?cè)趧?dòng)態(tài)添加方法, 屬性, 實(shí)例變量的時(shí)候一定要在objc_allocateClassPair和objc_registerClassPair兩個(gè)函數(shù)調(diào)用之間完成。
<6>創(chuàng)建對(duì)象, 調(diào)用方法
<7>銷(xiāo)毀創(chuàng)建的對(duì)象, 銷(xiāo)毀創(chuàng)建的類(lèi)
小結(jié):
這篇只是簡(jiǎn)單的介紹一下runtime對(duì)于類(lèi)和對(duì)象的一些簡(jiǎn)單的函數(shù), 幫助大家了解Objective-C低層的操作, 同時(shí)幫助大家對(duì)于元類(lèi)進(jìn)行理解, 具體的代碼會(huì)在下面的地址中給出, 大家可以參考代碼對(duì)于runtime中的函數(shù)有著進(jìn)一步的了解, 下一篇我們會(huì)講述利用runtime創(chuàng)建property和method swizzling, 讓大家在平時(shí)的編程中能夠用上runtime。
代碼下載地址:
https://github.com/CveniEs/Runtime.git
https://github.com/CveniEs/Runtime_Example.git
注:本門(mén)的很多內(nèi)容都參考與南峰子的博客, 有興趣同學(xué)可以去學(xué)習(xí)一下。
參考:
Objective-C Runtime Programming Guide
Objective-C Runtime Reference
南峰子技術(shù)博客