Objective-C對象之類對象和元類對象(一)

Objective-C對象之類對象和元類對象(一)

作者:wangzz
轉載請註明出處
如果覺得文章對你有所幫助,請通過留言或關注微信公衆帳號wangzzstrive來支持我,謝謝!

作爲C語言的超集,面向對象成爲Objective-C與C語言的最大區別,因此,對象是Objective-C中最重要的部分之一。目前面向對象的語言有很多,Objective-C中的對象又和其他語言中的對象有什麼區別呢?下面來簡單介紹Objective-C中對象的實現。
1、Objective-C中的類

誰都知道,所有的對象都是由其對應的類實例化而來,殊不知類本身也是一種對象,先不要對這句話感到驚訝。
首先我們來關注Objective-C中的類。在Objective-C中,我們用到的幾乎所有類都是NSObject類的子類,NSObject類定義格式如下(忽略其方法聲明):
@interface NSObject <NSObject> {
Class isa;
}
這個Class爲何物?在objc.h中我們發現其僅僅是一個結構(struct)指針的typedef定義:
typedef struct objc_class *Class;
同樣的,objc_class又是什麼呢?在Objective-C2.0中,objc_class的定義如下:
struct objc_class {
Class isa;
}
寫到這裏大家可能就暈了,怎麼又有一個isa??這些isa到底是什麼?之間有什麼區別和聯繫?接下來解答這一連串的疑問。
其實在Objective-C中任何的類定義都是對象。即在程序啓動的時候任何類定義都對應於一塊內存。在編譯的時候,編譯器會給每一個類生成一個且只生成一個”描述其定義的對象”,也就是蘋果公司說的類對象(class object),他是一個單例(singleton), 而我們在C++等語言中所謂的對象,叫做實例對象(instance object)。對於實例對象我們不難理解,但類對象(class object)是幹什麼吃的呢?我們知道Objective-C是門很動態的語言,因此程序裏的所有實例對象(instace object)都是在運行時由Objective-C的運行時庫生成的,而這個類對象(class object)就是運行時庫用來創建實例對象(instance object)的依據。
再回到之前的問題,腫麼這個實例對象(instance object)的isa指針指向的類對象(class object)裏面還有一個isa呢?這個類對象(class objec)的isa指向的依然是一個objc-class,它就是“元類對象”(metaclass object),它和類對象(class object)的關係是這樣的:

2、類對象(class object)
①類對象的實質
我們知道了:類對象是由編譯器創建的,即在編譯時所謂的類,就是指類對象(官方文檔中是這樣說的: The class object is the compiled version of the class)。任何直接或間接繼承了NSObject的類,它的實例對象(instance objec)中都有一個isa指針,指向它的類對象(class object)。這個類對象(class object)中存儲了關於這個實例對象(instace object)所屬的類的定義的一切:包括變量,方法,遵守的協議等等。因此,類對象能訪問所有關於這個類的信息,利用這些信息可以產生一個新的實例,但是類對象不能訪問任何實例對象的內容。
當你調用一個 “類方法” 例如 [NSObject alloc],你事實上是發送了一個消息給他的類對象。

②類對象和實例對象的區別
當然有區別了,儘管類對象保留了一個類實例的原型,但它並不是實例本身。它沒有自己的實例變量,也不能執行那些類的實例的方法(只有實例對象纔可以執行實例方法)。然而,類的定義能包含那些特意爲類對象準備的方法–類方法( 而不是的實例方法)。類對象從父類那裏繼承類方法,就像實例從父類那裏繼承實例方法一樣。
③類對象與類名
在源代碼中,類對象由類名錶示。
在下面的例子中,Retangle類 用從NSObject那裏繼承來的方法來返回類的版本號:
int versionNumber = [Rectangle version];
只有在消息表達式中作爲接收者,類名才代表類對象。其他地方,你需要要求一個實例或者類返回class id。 響應class消息:
id aClass = [anObject class];
id rectClass = [Rectangle class];
如同上面的例子顯示的那樣,類對象像其他對象一樣,也是id類型。

總之,類對象是一個功能完整的對象,所以也能被動態識別(dynamically typed),接收消息,從其他類繼承方法。特殊之處在於它們是由編譯器創建的,缺少它們自己的數據結構(實例變量),只是在運行時產生實例的代理。

3、元類對象(metaclass object)
①元類對象的實質
實際上,類對象是元類對象的一個實例!!元類描述了 一個類對象,就像類對象描述了普通對象一樣。不同的是元類的方法列表是類方法的集合,由類對象的選擇器來響應。當向一個類發送消息時,objc_msgSend會通過類對象的isa指針定位到元類,並檢查元類的方法列表(包括父類)來決定調用哪個方法。元類代替了類對象描述了類方法,就像類對象代替了實例對象描述了實例化方法。
很顯然,元類也是對象,也應該是其他類的實例,實際上元類是根元類(root class’s metaclass)的實例,而根元類是其自身的實例,即根元類的isa指針指向自身。
類的super_class指向其父類,而元類的super_class則指向父類的元類。元類的super class鏈與類的super class鏈平行,所以類方法的繼承與實例方法的繼承也是並行的。而根元類(root class’s metaclass)的super_class指向根類(root class),這樣,整個指針鏈就鏈接起來了!!

記住,當一個消息發送給任何一個對象, 方法的檢查 從對象的 isa 指針開始,然後是父類。實例方法在類中定義, 類方法 在元類和根類中定義。(根類的元類就是根類自己)。在一些計算機語言的原理中,一個類和元類層次結構可以更自由的組成,更深元類鏈和從單一的元類繼承的更多的實例化的類。Objective-C 的類方法 是使用元類的根本原因,在其他方面試圖在隱藏元類。例如 [NSObject class] 完全相等於 [NSObject self],所以,在形式上他還是返回的 NSObject->isa 指向的元類。 Objective-C語言是一組實用的折中方案。

還有些不明白? 下面這個圖標可能會有些幫助:



綜上所述,類對象(class object)中包含了類的實例變量,實例方法的定義,而元類對象(metaclass object)中包括了類的類方法(也就是C++中的靜態方法)的定義。類對象和元類對象中當然還會包含一些其它的東西,蘋果以後也可能添加其它的內容,但對於我們只需要記住:類對象存的是關於實例對象的信息(變量,實例方法等),而元類對象(metaclass object)中存儲的是關於類的信息(類的版本,名字,類方法等)。要注意的是,類對象(class object)和元類對象(metaclass object)的定義都是objc_class結構,其不同僅僅是在用途上,比如其中的方法列表在類對象(instance object)中保存的是實例方法(instance method),而在元類對象(metaclass object)中則保存的是類方法(class method)。關於元類對象可以參考蘋果官方文檔" The Objective-‐C Programming Language "

4、類對象和元類對象的相關方法

①object_getClass跟隨實例的isa指針,返回此實例所屬的類,對於實例對象(instance)返回的是類(class),對於類(class)則返回的是元類(metaclass),
②-class方法對於實例對象(instance)會返回類(class),但對於類(class)則不會返回元類(metaclass),而只會返回類本身,即[@"instance" class]返回的是__NSCFConstantString,而[NSString class]返回的是NSString。
③class_isMetaClass可判斷某類是否爲元類.                                     

④使用objc_allocateClassPair可在運行時創建新的類與元類對,使用class_addMethod和class_addIvar可向類中增加方法和實例變量,最後使用objc_registerClassPair註冊後,就可以使用此類了。看到動態語言牛逼的地方了嗎,可以在需要時更改已經定義好的類!Objective-C的類別方法估計底層就是這麼實現的,只是不知道爲什麼類別不能增加實例變量,有高手請留言。

----by wangzz


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章