前言
寫過iOS的人都知道runtime這個概念挖滤,那么OC在面向?qū)ο罄锩娴念惡蛯嵗约敖?jīng)常被提到的元類崩溪,這些到底和runtime是什么關(guān)系?又是如何實現(xiàn)的斩松?下面就來徹底的學(xué)習(xí)下這幾個概念伶唯。
這些概念是如何定義的
首先在你的xcode中打開 obc/runtime.h
頭文件,里面有一個結(jié)構(gòu)體定義:
struct object_class{
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息惧盹,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息乳幸,供運行期使用的一些位標(biāo)識
long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list *methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
}OBJC2_UNAVAILABLE;
結(jié)構(gòu)體的每個數(shù)據(jù)項都有注釋瞪讼,結(jié)構(gòu)體定義的已經(jīng)很清楚了,這個結(jié)構(gòu)體其實就是我們所說的 類 粹断,里面各項數(shù)據(jù)都保存了一個類的所有元數(shù)據(jù)符欠,然后我們先看下他的 Class isa
這個指針的類型是什么,點進去你會發(fā)現(xiàn)姿染,他是這樣定義的:
// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
原來isa指針指向的依然是個 object_class
結(jié)構(gòu)體背亥,只不過為了語義化起個名字叫Class
,那么現(xiàn)在我們再看下類實例是如何定義的:
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
因為我們初始化一個類的實例可以直接用id
來定義秒际,那么id
就是上面這樣定義的悬赏,所以類的實例初始化完了之后,它的內(nèi)部就多了一個isa
指針娄徊,剛我們已經(jīng)說了這個指針類型指向的是object_class
結(jié)構(gòu)體闽颇,其實也就是指向了這個實例所屬的類。
它們之間是如何聯(lián)系的
上面講了它們各自的定義寄锐,下面就來說說他們之間的關(guān)系兵多,其實很簡單當(dāng)我們創(chuàng)建一個對象的時候比如:
id objc = [[ClassA alloc] init];
我們的實例objc
里面包含了一個isa
指針,這個指針就指向了ClassA
類的元數(shù)據(jù)橄仆,因此我們就能用objc
對象去調(diào)用它的實例方法以及實例變量等功能剩膘,這樣我們就把實例和類聯(lián)系在了一起。
那么我們再說說元類盆顾,其實我們上面定義的ClassA
他其實也是一個對象怠褐,如果這里不好理解,你就想上面我們定義的實例objc
他要是想調(diào)用它的實例方法可以通過isa
指針去ClassA
里面去找您宪,那我們平時說的類方法是放在哪里的呢奈懒?答案就是ClassA
這個類里面的isa
指針也指向了一個object_class
結(jié)構(gòu)體,這個里面就放了ClassA
的類方法宪巨,因此也成它為元類
下面放一張圖來幫助理解下之間的關(guān)系:
所有元類都有一個根元類磷杏,比如所有NSObject的子類的元類都會以NSObject的元類作為他們的類。所有的元類使用根元類作為他們的類捏卓,根元類的元類則就是它自己极祸,也就是說根元類的isa指針指向他自己。
因此實例在尋找方法時的規(guī)則為:
- 當(dāng)發(fā)送消息給實例對象時怠晴,消息是在尋找這個對象的類的方法列表(實例方法)
- 當(dāng)發(fā)送消息給類對象時贿肩,消息是在尋找這個類的元類的方法列表(類方法)
示例測試:
People *p = [[People alloc] init];
//輸出1
NSLog(@"%d", [p class] == object_getClass(p));
//輸出0
NSLog(@"%d", class_isMetaClass(object_getClass(p)));
//輸出1
NSLog(@"%d", class_isMetaClass(object_getClass([People class])));
//輸出0
NSLog(@"%d", object_getClass(p) == object_getClass([People class]));
通過代碼可以看出,一個實例對象通過class方法獲取的Class就是它的isa指針指向的類對象龄寞,而類對象不是元類汰规,類對象的isa指針指向的對象是元類。
總結(jié)
總結(jié)一下實例對象物邑,類對象以及元類對象之間的isa指向和繼承關(guān)系的規(guī)則為:
- 規(guī)則一: 實例對象的isa指向該類溜哮,類的isa指向元類(metaClass)
- 規(guī)則二: 類的superClass指向其父類滔金,如果該類為根類則值為nil
- 規(guī)則三: 元類的isa指向根元類,如果該元類是根元類則指向自身
- 規(guī)則四: 元類的superClass指向父元類茂嗓,若根元類則指向該根類
其實實例餐茵、類、元類最主要的鏈接通道就是他們的isa
指針述吸,理解了值之間的概念就很容易掌握OC忿族,其實這個概念只是runtime概念中的一點前菜,runtime概念和應(yīng)用都是iOS開發(fā)中比較重要的蝌矛,所以object_class
這個結(jié)構(gòu)體中剩下的數(shù)據(jù)項我會在這篇文章中詳細(xì)講解道批。