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

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

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

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

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


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

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

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

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