本文**:
最近打算寫一些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 物件模...