1. 程式人生 > >iOS開發之關於Runtime執行時:類與物件

iOS開發之關於Runtime執行時:類與物件

Objective-C語言是一門動態語言,它將很多靜態語言在編譯和連結時期做的事放到了執行時來處理。這種動態語言的優勢在於:我們寫程式碼時更具靈活性,如我們可以把訊息轉發給我們想要的物件,或者隨意交換一個方法的實現等。

這種特性意味著Objective-C不僅需要一個編譯器,還需要一個執行時系統來執行編譯的程式碼。對於Objective-C來說,這個執行時系統就像一個作業系統一樣:它讓所有的工作可以正常的執行。這個執行時系統即Objc Runtime。Objc Runtime其實是一個Runtime庫,它基本上是用C和彙編寫的,這個庫使得C語言有了面向物件的能力。

  • Runtime庫主要做下面幾件事:

    1. 封裝:在這個庫中,物件可以用C語言中的結構體表示,而方法可以用C函式來實現,另外再加上了一些額外的特性。這些結構體和函式被runtime函式封裝後,我們就可以在程式執行時建立,檢查,修改類、物件和它們的方法了。
    2. 找出方法的最終執行程式碼:當程式執行[object doSomething]時,會向訊息接收者(object)傳送一條訊息(doSomething),runtime會根據訊息接收者是否能響應該訊息而做出不同的反應。

Objective-C runtime目前有兩個版本:Modern runtime和Legacy runtime。Modern Runtime 覆蓋了64位的Mac OS X Apps,還有 iOS Apps,Legacy Runtime 是早期用來給32位 Mac OS X Apps 用的。

我們將介紹runtime的基本工作原理,以及如何利用它讓我們的程式變得更加靈活。在本文中,我們先來介紹一下類與物件,這是面向物件的基礎,我們看看在Runtime中,類是如何實現的。

類與物件基礎資料結構

Objective-C類是由Class型別來表示的,它實際上是一個指向objc_class結構體的指標。它的定義如下:

<code class="hljs d has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">typedef</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> objc_class *<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span>;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

檢視objc/runtime.h中objc_class結構體的定義如下:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> objc_class {

    Class isa  OBJC_ISA_AVAILABILITY;



<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">if</span> !__OBJC2__</span>

    Class super_class                       OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 父類</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> *name                        OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 類名</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> version                            OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 類的版本資訊,預設為0</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> info                               OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 類資訊,供執行期使用的一些位標識</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">long</span> instance_size                      OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 該類的例項變數大小</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 該類的成員變數連結串列</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> objc_method_list **methodLists   OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 方法定義的連結串列</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> objc_cache *cache                OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 方法快取</span>

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 協議連結串列</span>

<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#<span class="hljs-keyword" style="box-sizing: border-box;">endif</span></span>

} OBJC2_UNAVAILABLE;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul>

在這個定義中,下面幾個欄位是我們感興趣的

isa:需要注意的是在Objective-C中,所有的類自身也是一個物件,這個物件的Class裡面也有一個isa指標,它指向metaClass(元類),我們會在後面介紹它。 
super_class:指向該類的父類,如果該類已經是最頂層的根類(如NSObject或NSProxy),則super_class為NULL。 
cache:用於快取最近使用的方法。一個接收者物件接收到一個訊息時,它會根據isa指標去查詢能夠響應這個訊息的物件。在實際使用中,這個物件只有一部分方法是常用的,很多方法其實很少用或者根本用不上。這種情況下,如果每次訊息來時,我們都是methodLists中遍歷一遍,效能勢必很差。這時,cache就派上用場了。在我們每次呼叫過一個方法後,這個方法就會被快取到cache列表中,下次呼叫的時候runtime就會優先去cache中查詢,如果cache沒有,才去methodLists中查詢方法。這樣,對於那些經常用到的方法的呼叫,但提高了呼叫的效率。 
version:我們可以使用這個欄位來提供類的版本資訊。這對於物件的序列化非常有用,它可是讓我們識別出不同類定義版本中例項變數佈局的改變。 
針對cache,我們用下面例子來說明其執行過程:

針對cache,我們用下面例子來說明其執行過程:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span> *array = [[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span> alloc] init];</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

其流程是:

  1. [NSArray alloc]先被執行。因為NSArray沒有+alloc方法,於是去父類NSObject去查詢。
  2. 檢測NSObject是否響應+alloc方法,發現響應,於是檢測NSArray類,並根據其所需的記憶體空間大小開始分配記憶體空間,然後把isa指標指向NSArray類。同時,+alloc也被加進cache列表裡面。
  3. 接著,執行-init方法,如果NSArray響應該方法,則直接將其加入cache;如果不響應,則去父類查詢。
  4. 在後期的操作中,如果再以[[NSArray alloc] init]這種方式來建立陣列,則會直接從cache中取出相應的方法,直接呼叫。

objc_object與id

objc_object是表示一個類的例項的結構體,它的定義如下(objc/objc.h):

<code class="hljs scala has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">struct objc_<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">object</span> {</span>

    Class isa  OBJC_ISA_AVAILABILITY;

};

typedef struct objc_<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">object</span> *<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">id</span>;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

可以看到,這個結構體只有一個字型,即指向其類的isa指標。這樣,當我們向一個Objective-C物件傳送訊息時,執行時庫會根據例項物件的isa指標找到這個例項物件所屬的類。Runtime庫會在類的方法列表及父類的方法列表中去尋找與訊息對應的selector指向的方法。找到後即執行這個方法。

當建立一個特定類的例項物件時,分配的記憶體包含一個objc_object資料結構,然後是類的例項變數的資料。NSObject類的alloc和allocWithZone:方法使用函式class_createInstance來建立objc_object資料結構。

另外還有我們常見的id,它是一個objc_object結構型別的指標。它的存在可以讓我們實現類似於C++中泛型的一些操作。該型別的物件可以轉換為任何一種物件,有點類似於C語言中void *指標型別的作用。

objc_cache

上面提到了objc_class結構體中的cache欄位,它用於快取呼叫過的方法。這個欄位是一個指向objc_cache結構體的指標,其定義如下

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> objc_cache {

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> mask <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* total = mask + 1 */</span>                 OBJC2_UNAVAILABLE;

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> occupied                                    OBJC2_UNAVAILABLE;

    Method buckets[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>]                                        OBJC2_UNAVAILABLE;

};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

該結構體的欄位描述如下:

  1. mask:一個整數,指定分配的快取bucket的總數。在方法查詢過程中,Objective-C runtime使用這個欄位來確定開始線性查詢陣列的索引位置。指向方法selector的指標與該欄位做一個AND位操作(index = (mask & selector))。這可以作為一個簡單的hash雜湊演算法。
  2. occupied:一個整數,指定實際佔用的快取bucket的總數。
  3. buckets:指向Method資料結構指標的陣列。這個陣列可能包含不超過mask+1個元素。需要注意的是,指標可能是NULL,表示這個快取bucket沒有被佔用,另外被佔用的bucket可能是不連續的。這個陣列可能會隨著時間而增長。

元類(Meta Class)

在上面我們提到,所有的類自身也是一個物件,我們可以向這個物件傳送訊息(即呼叫類方法)。如:

<code class="hljs php has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">NSArray *<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">array</span> = [NSArray <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">array</span>];</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

這個例子中,+array訊息傳送給了NSArray類,而這個NSArray也是一個物件。既然是物件,那麼它也是一個objc_object指標,它包含一個指向其類的一個isa指標。那麼這些就有一個問題了,這個isa指標指向什麼呢?為了呼叫+array方法,這個類的isa指標必須指向一個包含這些類方法的一個objc_class結構體。這就引出了meta-class(元類)的概念

meta-class是一個類物件的類。 
當我們向一個物件傳送訊息時,runtime會在這個物件所屬的這個類的方法列表中查詢方法;而向一個類傳送訊息時,會在這個類的meta-class的方法列表中查詢。

meta-class之所以重要,是因為它儲存著一個類的所有類方法。每個類都會有一個單獨的meta-class,因為每個類的類方法基本不可能完全相同。

再深入一下,meta-class也是一個類,也可以向它傳送一個訊息,那麼它的isa又是指向什麼呢?為了不讓這種結構無限延伸下去,Objective-C的設計者讓所有的meta-class的isa指向基類的meta-class,以此作為它們的所屬類。即,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class作為自己的所屬類,而基類的meta-class的isa指標是指向它自己。這樣就形成了一個完美的閉環。 
通過上面的描述,再加上對objc_class結構體中super_class指標的分析,我們就可以描繪出類及相應meta-class類的一個繼承體系了,如下圖所示: 
這裡寫圖片描述

對於NSObject繼承體系來說,其例項方法對體系中的所有例項、類和meta-class都是有效的;而類方法對於體系內的所有類和meta-class都是有效的

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//建立類,分析類與元類的關係</span>

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> TestMetaClass(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>, SEL _cmd) {

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"This objcet is %p"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span>);

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Class is %@, super class is %@"</span>, [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> class], [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> superclass]);

    Class currentClass = [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span> class];

    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> i = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; i < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span>; i++) {

        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Following the isa pointer %d times gives %p"</span>, i, currentClass);

        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//objc_getClass 得到物件的isa指標</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//objc_getClass  isa 錯誤</span>
        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//object_getClass 正確的</span>
        currentClass = object_getClass(currentClass);

    }

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"NSObject's class is %p"</span>, [<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSObject</span> class]);

    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"NSObject's meta class is %p"</span>, object_getClass([<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSObject</span> class]));

}


- (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)ex_registerClassPair {

    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//建立一個類</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//Class objc_allocateClassPair(Class superclass, const char *name,size_t extraBytes)</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//superclass 父類的名字</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//name 類名</span>

    Class newClass = objc_allocateClassPair([<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSError</span> class], <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"TestAAAClass"</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);

    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//給這個類新增方法</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp,const char *types)</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//cls  新增方法的類</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//name  方法名</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//imp 方法的指標</span>
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//types:“[email protected]:” void id cmd</span>
    class_addMethod(newClass, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@selector</span>(testMetaClass), (IMP)TestMetaClass, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"[email protected]:"</span>);
    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//註冊類</span>
    objc_registerClassPair(newClass);


    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> instance = [[newClass alloc] initWithDomain:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"some domain"</span> code:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span> userInfo:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>];

    [instance performSelector:<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@selector</span>(testMetaClass)];

}
</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li></ul>

我們在for迴圈中,我們通過objc_getClass來獲取物件的isa,並將其打印出來,依此一直回溯到NSObject的meta-class。分析列印結果,可以看到最後指標指向的地址是0x0,即NSObject的meta-class的類地址。

這裡需要注意的是:我們在一個類物件呼叫class方法是無法獲取meta-class,它只是返回類而已。

類與物件操作函式

runtime提供了大量的函式來操作類與物件。類的操作方法大部分是以class為字首的,而物件的操作方法大部分是以objc或object_為字首。下面我們將根據這些方法的用途來分類討論這些方法的使用。

類相關操作函式

我們可以回過頭去看看objc_class的定義,runtime提供的操作類的方法主要就是針對這個結構體中的各個欄位的。下面我們分別介紹這一些的函式。並在最後以例項來演示這些函式的具體用法

類名(name)

類名操作的函式主要有:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 獲取類的類名</span>

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> * class_getName ( Class cls );</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

● 對於class_getName函式,如果傳入的cls為Nil,則返回一個字字串

父類(super_class)和元類(meta-class)

父類和元類操作的函式主要有:

<code class="hljs scala has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 獲取類的父類</span>

Class class_getSuper<span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">( Class cls )</span>;</span>



<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 判斷給定的Class是否是一個元類</span>

BOOL class_isMetaClass ( Class cls );</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

● class_getSuperclass函式,當cls為Nil或者cls為根類時,返回Nil。不過通常我們可以使用NSObject類的superclass方法來達到同樣的目的。

● class_isMetaClass函式,如果是cls是元類,則返回YES;如果否或者傳入的cls為Nil,則返回NO。

例項變數大小(instance_size)

例項變數大小操作的函式有:

<code class="hljs php has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 獲取例項大小</span>

size_t class_getInstanceSize ( <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">Class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">cls</span> );</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

成員變數(ivars)及屬性 
在objc_class中,所有的成員變數、屬性的資訊是放在連結串列ivars中的。ivars是一個數組,陣列中每個元素是指向Ivar(變數資訊)的指標。runtime提供了豐富的函式來操作這一欄位。大體上可以分為以下幾類:

1.成員變數操作函式,主要包含以下函式:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 獲取類中指定名稱例項成員變數的資訊</span>

Ivar class_getInstanceVariable ( Class cls, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> *name );

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 獲取類成員變數的資訊</span>

Ivar class_getClassVariable ( Class cls, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> *name );



<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 新增成員變數</span>

<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">BOOL</span> class_addIvar ( Class cls, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> *name, size_t size, uint8_t alignment, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> *types );



<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// 獲取整個成員變數列表</span>

Ivar * class_copyIvarList ( Class cls, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> *outCount );</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: borde