Objective-C 是一門面向?qū)ο蟮某绦蛟O(shè)計(jì)語言夭织,它的對(duì)象模型是基于類來建立的仁锯。我們可以在這里下載最新的runtime源碼掐禁,當(dāng)前最新版本為objc4-709.tar.gz
Objective-C中的對(duì)象
NSObject
是Objective-C中的基類未玻,NSObject
定義在objc4-709/runtime/NSObject.h
中:
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
在NSObject
的定義中,我們發(fā)現(xiàn) NSObject
只有一個(gè)Class
類型的成員變量饭寺,同樣在objc4-709/runtime/objc-private.h
中可以找到Class
的定義:
typedef struct objc_class *Class;
Class
其實(shí)也是一個(gè)結(jié)構(gòu)體阻课,也就是說Objective-C中的類其實(shí)也是一個(gè)結(jié)構(gòu)體。繼續(xù)找下去:
在 objc4-709/runtime/objc-runtime-new
中艰匙,能看到objc_class的定義:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
...
...
...
}
objc_class
是繼承自objc_object
的限煞,所以O(shè)bjective-C中的類,其實(shí)也是一個(gè)對(duì)象员凝。
在objc4-709/runtime/objc-private.h
中可以找到objc_object
對(duì)象的定義:
struct objc_object {
private:
isa_t isa;
public:
Class ISA();
Class getIsa();
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
Class changeIsa(Class newCls);
...
...
...
};
在Objective-C 中最基本的數(shù)據(jù)結(jié)構(gòu)就是:struct objc_object
Objective-C中的對(duì)象本質(zhì)上是一個(gè)結(jié)構(gòu)體
id類型
在同一個(gè)文件中署驻,我們還能找到Objective-C中id
的定義:
typedef struct objc_object *id;
能看到, id
其實(shí)就是一個(gè) struct objc_object
類型的指針绊序。但是 id
不是 NSObject *
硕舆,為什么可以指向任意的 NSObject
對(duì)象呢?因?yàn)?objc_object
只有一個(gè) Class
類型的 isa
變量骤公,而這個(gè)結(jié)構(gòu),恰恰和 NSObject
的定義一致扬跋。
這樣阶捆,NSObject
對(duì)象的首地址處的內(nèi)容,就能用id指向了钦听。因?yàn)楸举|(zhì)上洒试,對(duì)象的數(shù)據(jù)都是由struct
存放的。
Class
對(duì)象的類不僅描述了對(duì)象的數(shù)據(jù):對(duì)象占用的內(nèi)存大小朴上、成員變量的類型和布局等垒棋,而且也描述了對(duì)象的行為:對(duì)象能夠響應(yīng)的消息、實(shí)現(xiàn)的實(shí)例方法等痪宰。
在 Objective-C 中有一個(gè)非常特殊的類 NSObject 叼架,絕大部分的類都繼承自它畔裕。它是 Objective-C 中的兩個(gè)根類(rootclass)之一,另外一個(gè)是 NSProxy乖订。同樣的扮饶,我們打開文件 NSObject.h ,可以看到 NSObject 類其實(shí)就只有一個(gè)成員變量 isa 乍构,所有繼承自 NSObject 的類也都會(huì)有這個(gè)成員變量甜无。
如果把類的實(shí)例看成一個(gè) C 語言的結(jié)構(gòu)體(struct),上面說的 isa 指針就是這個(gè)結(jié)構(gòu)體的第一個(gè)成員變量哥遮,而類的其它成員變量依次排列在結(jié)構(gòu)體中岂丘。排列順序如下圖所示:
對(duì)象的實(shí)例方法調(diào)用時(shí),通過對(duì)象的 isa 在類中獲取方法的實(shí)現(xiàn)眠饮。
類對(duì)象的類方法調(diào)用時(shí)奥帘,通過類的 isa 在元類中獲取方法的實(shí)現(xiàn)。
當(dāng)一個(gè)對(duì)象的實(shí)例方法被調(diào)用的時(shí)候君仆,會(huì)通過isa找到相應(yīng)的類翩概,然后在該類的class_data_bits_t中去查找方法。class_data_bits_t是指向了類對(duì)象的數(shù)據(jù)區(qū)域返咱。在該數(shù)據(jù)區(qū)域內(nèi)查找相應(yīng)方法的對(duì)應(yīng)實(shí)現(xiàn)钥庇。
元類(meta-class)之所以重要,是因?yàn)樗鎯?chǔ)著一個(gè)類的所有類方法咖摹。每個(gè)類都會(huì)有一個(gè)單獨(dú)的meta-class评姨,因?yàn)槊總€(gè)類的類方法基本不可能完全相同。
元類(metaclass)
因?yàn)轭愐彩且粋€(gè)對(duì)象萤晴,那么類也必須是另一個(gè)類的實(shí)例吐句,有點(diǎn)繞,可以理解成Class
是元類的對(duì)象店读。元類保存了類方法的列表嗦枢。當(dāng)一個(gè)類的方法被調(diào)用時(shí),元類會(huì)首先查找它本身是否有該類方法的實(shí)現(xiàn)屯断,如果沒有文虏,則該元類會(huì)向它的父類查找該方法,直到一直找到繼承鏈的頭殖演。
元類 (metaclass) 也是一個(gè)對(duì)象氧秘,那么元類的 isa 指針又指向哪里呢?為了設(shè)計(jì)上的完整趴久,所有的元類的 isa 指針都會(huì)指向一個(gè)根元類 (root metaclass)丸相。根元類 (root metaclass) 本身的 isa 指針指向自己,這樣就行成了一個(gè)閉環(huán)彼棍。上面提到灭忠,一個(gè)對(duì)象能夠接收的消息列表是保存在它所對(duì)應(yīng)的類中的膳算。在實(shí)際編程中,我們幾乎不會(huì)遇到向元類發(fā)消息的情況更舞,那它的 isa 指針在實(shí)際上很少用到畦幢。不過這么設(shè)計(jì)保證了面向?qū)ο蟮母蓛簦此惺挛锒际菍?duì)象缆蝉,都有 isa 指針宇葱。
對(duì)應(yīng)關(guān)系的圖如下圖,下圖很好的描述了對(duì)象刊头,類黍瞧,元類之間的關(guān)系:
(圖片源自 這里)
我們?cè)賮砜纯蠢^承關(guān)系,由于類方法的定義是保存在元類 (metaclass
) 中原杂,而方法調(diào)用的規(guī)則是印颤,如果該類沒有一個(gè)方法的實(shí)現(xiàn),則向它的父類繼續(xù)查找穿肄。所以年局,為了保證父類的類方法可以在子類中可以被調(diào)用,所以子類的元類會(huì)繼承父類的元類咸产,換而言之矢否,類對(duì)象和元類對(duì)象有著同樣的繼承關(guān)系。
既然類是一個(gè)對(duì)象脑溢,那么元類是一個(gè)對(duì)象嗎僵朗?其實(shí)元類也是一個(gè)對(duì)象,它是根元類(root metaclass)的實(shí)例屑彻。
那么验庙,根元類呢?這么循環(huán)下去什么時(shí)候是個(gè)頭社牲。其實(shí)根元類所屬的類也是根元類粪薛,即它本身。根元類指的就是根類的元類搏恤,也就是根類 NSObject
的元類汗菜。