深入淺出Cocoa之類與物件

2021-06-06 16:56:12 字數 3661 閱讀 6768

本文**:

最近打算寫一些objc中比較底層的東西,尤其是 runtime 相關的。蘋果已經將 objc runtime **開源了,

我們可以從:

瀏覽源**,或

源**。

從**入手呢?那當然是最基本的類與物件。與c++相比,objc中的類與物件結構要簡潔與一致得多(參考《深度探索c++物件模型》,你就知道c++中類與物件結構的複雜)。本文將詳細講解objc中類與物件的結構,下回將講如何在 runtime 時操作類。

我們可以在/usr/include/objc/objc.h 和 runtime.h 中找到對 class 與 object 的定義:

typedef 

struct objc_class *class;

typedef 

struct objc_object  *id;

class 是乙個 objc_class 結構型別的指標;而 id(任意物件) 是乙個 objc_object 結構型別的指標,其第乙個成員是乙個 objc_class 結構型別的指標。注意這裡有一關鍵的引申解讀:記憶體布局以乙個 objc_class 指標為開始的所有東東都可以當做乙個 object 來對待!那 objc_class 又是怎樣乙個結構體呢?且看:

struct objc_class ;

objc_class 結構體的各成員介紹如下:

isa :是乙個 objc_class 型別的指標,看到這裡,想起我前面的引申解讀了沒?記憶體布局以乙個 objc_class 指標為開始的所有東東都可以當做乙個 object 來對待!這就是說 objc_class 或者說類其實也可以當做乙個 objc_object 物件來對待!物件是物件,類也是物件,是不是有點混淆?別急,objc發明(or 重用)了乙個術語來區分這兩種不同的物件:類物件(class object)與例項物件(instance object)。ok,名稱混淆的問題解決,下面我將使用這兩個術語來區分不同的物件,而使用「物件」這一術語來泛指所有的物件。objc還對類物件與例項物件中的 isa 所指向的類結構作了不同的命名:類物件中的 isa 指向類結構被稱作 metaclass,metaclass 儲存類的static類成員變數與static類成員方法(+開頭的方法);例項物件中的 isa 指向類結構稱作 class(普通的),class 結構儲存類的普通成員變數與普通成員方法(-開頭的方法)。

super_class

:一看就明白,指向該類的父類唄!如果該類已經是最頂層的根類(如 nsobject 或 nsproxy),那麼 super_class 就為 null。

好,先中斷一下其他類結構成員的介紹,讓我們釐清一下在繼承層次中,子類,父類,根類(這些都是普通 class)以及其對應的 metaclass 的 isa 與 super_class 之間關係:

規則一:類的例項物件的 isa 指向該類;該類的 isa 指向該類的 metaclass;

規則二:類的 super_class 指向其父類,如果該類為根類則值為 null;

規則三:metaclass 的 isa 指向根 metaclass,如果該 metaclass 是根 metaclass 則指向自身;

規則四:metaclass 的 super_class 指向父 metaclass,如果該 metaclass 是根 metaclass 則指向該 metaclass 對應的類;

好吧,文字總是那麼乏力,有圖有真相!

nsstring * str;

[str lowercasestring];

metaclass 是 class object 的類型別。當我們向類物件傳送訊息(類方法)時,我們在該類物件的 metaclass 結構的 methodlists 中去查詢響應的函式,如果沒有找到匹配的響應函式則在該 metaclass 的父類中的 methodlists 去查詢(查詢鏈為上圖的最右邊那一排)。如下面的**中,向 nsstring 類物件傳送 stringwithstring 訊息,會在 nsstring 的 metaclass 類結構的 methodlists 中去查詢 stringwithstring 的響應函式。

[nsstring stringwithstring:@"str"];

好,至此我們明白了類的結構層次,讓我們接著看類結構中的其他成員。

name

:乙個 c 字串,指示類的名稱。我們可以在執行期,通過這個名稱查詢到該類(通過:id objc_getclass(const char *aclassname))或該類的 metaclass(id objc_getmetaclass(const char *aclassname));

version

:類的版本資訊,預設初始化為 0。我們可以在執行期對其進行修改(class_setversion)或獲取(class_getversion)。

info

:供執行期使用的一些位標識。有如下一些位掩碼:

cls_class (0x1l) 表示該類為普通 class ,其中包含例項方法和變數;

cls_meta (0x2l) 表示該類為 metaclass,其中包含類方法;

cls_initialized (0x4l) 表示該類已經被執行期初始化了,這個標識位只被 objc_addclass 所設定;

cls_posing (0x8l) 表示該類被 pose 成其他的類;(poseclass 在objc 2.0中被廢棄了);

cls_flush_cache (0x20l) 為objc執行期所使用

cls_grow_cache (0x40l) 為objc執行期所使用

cls_need_bind (0x80l) 為objc執行期所使用

cls_method_array (0x100l) 該標誌位指示 methodlists 是指向乙個 objc_method_list 還是乙個包含 objc_method_list 指標的陣列;

instance_size:該類的例項變數大小(包括從父類繼承下來的例項變數);

ivars

:指向 objc_ivar_list 的指標,儲存每個例項變數的記憶體位址,如果該類沒有任何例項變數則為 null;

methodlists

:與 info 的一些標誌位有關,cls_method_array 標識位決定其指向的東西(是指向單個 objc_method_list還是乙個 objc_method_list 指標陣列),如果 info 設定了 cls_class 則 objc_method_list  儲存例項方法,如果設定的是 cls_meta 則儲存類方法;

cache

:指向 objc_cache 的指標,用來快取最近使用的方法,以提高效率;

protocols

:指向 objc_protocol_list 的指標,儲存該類宣告要遵守的正式協議。

總結

:objc 為每個類的定義生成兩個 objc_class ,乙個即普通的 class,另乙個即 metaclass。我們可以在執行期建立這兩個 objc_class 資料結構,然後使用 objc_addclass 動態地建立新的類定義。這個夠動態夠強大的吧?下回講演示如何在執行期動態建立新類。

深入淺出Cocoa之類與物件

從 入手呢?那當然是最基本的類與物件。與c 相比,objc中的類與物件結構要簡潔與一致得多 參考 深度探索c 物件模型 你就知道c 中類與物件結構的複雜 本文將詳細講解objc中類與物件的結構,下回將講如何在 runtime 時操作類。我們可以在 usr include objc objc.h 和 ...

深入淺出Cocoa之類與物件

深入淺出cocoa之類與物件 羅朝輝 從 入手呢?那當然是最基本的類與物件。與c 相比,objc中的類與物件結構要簡潔與一致得多 參考 深度探索c 物件模型 你就知道c 中類與物件結構的複雜 本文將詳細講解objc中類與物件的結構,下回將講如何在 runtime 時操作類。我們可以在 usr inc...

深入淺出Cocoa之類與物件

深入淺出cocoa之類與物件 羅朝輝 最近打算寫一些objc中比較底層的東西,尤其是 runtime 相關的。蘋果已經將 objc runtime 開源了,我們可以從 瀏覽源 或 源 從 入手呢?那當然是最基本的類與物件。與c 相比,objc中的類與物件結構要簡潔與一致得多 參考 深度探索c 物件模...