meta-class是什么碴开?

在這篇文章中叹螟,我關(guān)注的是 Objective-C 中的一個陌生的概念—— meta-class。在 Objective-C 中的每個類都有一個相關(guān)聯(lián)的 meta-class畏线,但是你很少會直接使用 meta-class寝殴,他們?nèi)耘f保持著神秘的面紗。我們從在運行時創(chuàng)建一個類開始市咽。通過查看 “class pair”抵蚊,我會解釋 meta-class 是什么,同時也會談?wù)勗?Objective-C 中的對象或者類相關(guān)的一些一般主題谷醉。
在運行時創(chuàng)建一個類
下面的代碼在運行時創(chuàng)建了一個 NSError 的子類同時為它添加了一個方法:

Class newClass =objc_allocateClassPair([NSError class],"RuntimeErrorSubclass", 0);

class_addMethod(newClass, @selector(report),(IMP)ReportFunction, "v@:");

objc_registerClassPair(newClass);

添加的方法使用叫 ReportFunction 的函數(shù)作為實現(xiàn)俱尼,定義如下:

void ReportFunction(id self, SEL _cmd)
{
    NSLog(@"This object is %p.", self);
    NSLog(@"Class is %@, and super is %@.", [self class], [self superclass]);

    Class currentClass = [self class];
    for (int i = 1; i < 5; i++)
    {
        NSLog(@"Following the isa pointer %d times gives %p", i, currentClass);
        currentClass = object_getClass(currentClass);
    }

    NSLog(@"NSObject's class is %p", [NSObject class]);
    NSLog(@"NSObject's meta class is %p", object_getClass([NSObject class]));
}

表面上來看遇八,非常簡單刃永。在運行時創(chuàng)建一個類只需要這三步:

1羊精、為“classpair” 創(chuàng)建存儲空間(使用objc_allocateClassPair)。2、為這個類添加所需的methods 和ivars(我已經(jīng)使用class_addMethod 添加過一個方法了)裸违。3本昏、注冊這個類涌穆,然后就可以使用了(使用objc_registerClassPair)。

然后趁舀,中級問題是:“classpair” 是什么矮烹?函數(shù)objc_allocateClassPair 只返回了一個值:這個 class。這一對中的另一個在哪奉狈?(譯注:pair 有“一對,一雙” 的意思)

我敢肯定你已經(jīng)猜到了另一半就是meta-class(就是這篇文章的標題)桑驱,但是要解釋那是什么和你為什么需要它熬的,我需要介紹一些在Objective-C 中的關(guān)于對象和類的背景知識问芬。

把一個數(shù)據(jù)結(jié)構(gòu)變?yōu)閷ο笮枰裁矗?/h4>

每個對象都有一個類此衅。這是面相對象概念的基礎(chǔ)知識,但在Objective-C 中不是這樣骑歹,它(譯注:class)同樣是這個數(shù)據(jù)的一部分道媚。每個可以被當成對象的數(shù)據(jù)結(jié)構(gòu)都在恰當?shù)奈恢糜幸粋€指向一個類的指針翘县。

在Objective-C,一個對象的類由它的isa 指針決定镀脂。isa 指針指向這個對象的Class薄翅。

事實上氓奈,在Objective-C 中的對象的定義看起來像這樣:

typedef struct objc_object {
    Class isa;
} *id;

這就是說:任何結(jié)構(gòu)體只要以一個指向 Class 結(jié)構(gòu)的指針開始的就可以被當成是 objc_object舀奶。

在 Objective-C 中的對象的一個重要的特性是,你可以向它們發(fā)送消息:

[@"stringValue"
    writeToFile:@"/file.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL];

你可以這么做是因為光羞,當你向一個 Objective-C 的對象(像這里的 NSCFString)發(fā)送消息的時候,runtime 沿著對象的 isa 指針找到了這個對象的 Class(這里是 NSCFString 的類)結(jié)構(gòu)體呀闻。 Class 結(jié)構(gòu)體中包含了一個這個類的方法列表和一個指向父類的指針捡多,用于查找繼承的方法铐炫。

關(guān)鍵點是 Class 結(jié)構(gòu)體中定義了你可以向一個對象發(fā)送的消息倒信。

meta-class 是什么?

現(xiàn)在榜掌,你可能已經(jīng)知道乘综,在 Objective-C 中一個 Class 也是一個對象卡辰。這就意味著你也可以向一個 Class 發(fā)送消息。

NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];

這里反砌,向 NSString 類發(fā)送了 defaultStringEncoding。

可以這么做是因為在 Objective-C 中每個 Class 它自己同樣也是個對象于颖。也就是說 Class 結(jié)構(gòu)體必須以 isa 指針開始嚷兔,然后就可以在二進制兼容(binary compatible)我上面介紹的 objc_object 結(jié)構(gòu)了做入,接著下一個字段必須是一個指向它的父類的指針(要是類就是基類就是 nil)竟块。

我上周已經(jīng)介紹過,定義一個類有好幾種方法蒋情,主要依賴于你正在運行的 runtime 的版本。但辕翰,是的狈谊,都是由一個 isa 字段開始然后是 superclass 字段。

typedef struct objc_class *Class;
struct objc_class {
    Class isa;
    Class super_class;
    /* 以下依賴于 runtime 的具體實現(xiàn) …… */
};

然而壁榕,為了讓我們在 Class 上調(diào)用一個方法,Class 的 isa 指針必須指向一個 Class 結(jié)構(gòu)體牌里,并且那個 Class 結(jié)構(gòu)體必須包含我們可以在那個 Class 上調(diào)用的方法的列表务甥。

這就引出了 meta-class 的定義:meta-class 是 Class 對象的類(the meta-class is the class for a Class object)。

簡單來說:

當你向一個對象發(fā)送消息催享,就在那個對象的方法列表中查找那個消息哟绊。
當你想一個類發(fā)送消息,就再那個類的 meta-class 中查找那個消息攀涵。

meta-class 是必須的洽沟,因為它為一個 Class 存儲類方法。每個類都必須有一個唯一的 meta-class裆操,因為每個 Class 都有一個可能不一樣的類方法。

meta-class 的類是什么踪区?

meta-class缎岗,如之前的 Class,同樣是個對象。這就意味著你也可以在它上面調(diào)用方法鸭巴。自然的鹃祖,這就意味著它也必須有一個類(譯注:isa 指針)掌敬。

所有的 meta-class 使用它們基類的 meta-class (繼承層次中最頂層的 Class 的 meta-class)作為它們自己的類。這就是說所有繼承自 NSObject 的類(大部分的類)奔害,以 NSObject 的 meta-class 作為自己的 meta-class 的類。

遵循這個規(guī)則华临,所有的 meta-class 使用基類的 meta-class 作為他們的類,任何基類的 meta-class 將會是他們自己(它們的 isa 指向他們自己)揭厚。這就是說 NSObject 的 meta-class 的 isa 指針指向它們自己(是自己的一個實例)筛圆。

class 和 meta-class 的繼承

和 Class 以 super_class 指針指向它的父類的方法一樣椿浓,meta-class 以 super_class 指針指向 Class 的 super_class 的 meta-class。(譯注:這句話有點繞提岔,就是 super-class 一個指向 Class 的父類碱蒙,一個指向 meta-class 的父類夯巷。Class 是一般對象的類型赛惩,meta-class 是 Class 的類型喷兼。)

進一步來講,基類的 meta-class 設(shè)置 super_class 指針指向基類自己。

這個繼承層次的結(jié)果就是喷面,所有在這個繼承層次中的的實例,類和 meta-class 都繼承了基類的層次琳状。

對于所有在 NSObject 層次中的實例盒齿,類和 meta-class边翁,這就意味著所有 NSObject 的實例方法都是有效的。對于類和 meta-class叨咖,所有 NSObject 的類方法也同樣是有效的啊胶。

所有這些在字面上相當讓人困惑。Greg Parker 已經(jīng)把實例趣倾,類儒恋,meta-class 還有他們的超類以非常棒的圖解的方式聚合在一起露乏,展示他們是如何在一起工作的。
用實驗驗證這點

為了驗證這些箱锐,讓我們看看在我文章開頭提供的 ReportFunction 的輸出劳较。這個函數(shù)的目的是順著 isa 指針打引出它找到的。

要運行 ReportFunction臊恋,我們需要為這個動態(tài)創(chuàng)建的類創(chuàng)建一個實例抖仅,然后在上面調(diào)用這個方法。

id instanceOfNewClass =
    [[newClass alloc] initWithDomain:@"someDomain" code:0 userInfo:nil];
[instanceOfNewClass performSelector:@selector(report)];
[instanceOfNewClass release];

因為沒有這個方法的聲明环凿,所以我使用 performSelector: 調(diào)用這個方法放吩,這樣編譯器就不會輸出警告了渡紫。

現(xiàn)在 ReportFunction 會沿著 isa 指針告訴我們這個對象使用了哪些類,meta-class 和 meta-class 的類莉测。

獲取一個對象的類:ReportFunction 使用 object_getClass 跟隨 isa 指針悔雹,因為 isa 指針是一個類中一個受保護的成員變量(你不能直接訪問其他對象的 isa 指針)欣喧。ReportFunction 沒有以類方法的形式這樣調(diào)用,因為在 Class 對象上調(diào)用類方法不會返回 meta-class益涧,而是再次返回 Class 對象(所以 [NSString class] 會返回 NSString 的類而不是 NSString 的 meta-class)闲询。

這個是程序運行后的結(jié)果(省去了 NSLog 的前綴)浅辙。

This object is 0x10010c810.
Class is RuntimeErrorSubclass, and super is NSError.
Following the isa pointer 1 times gives 0x10010c600
Following the isa pointer 2 times gives 0x10010c630
Following the isa pointer 3 times gives 0x7fff71038480
Following the isa pointer 4 times gives 0x7fff71038480
NSObject's class is 0x7fff710384a8
NSObject's meta class is 0x7fff71038480

看看通過遞歸的查看 isa 的地址:

  • the object is address 0x10010c810.
  • the class is address 0x10010c600.
  • the meta-class is address 0x10010c630.
  • the meta-class’s class (i.e. the NSObject meta-class) is address 0x7fff71038480.
  • the NSObject meta-class’ class is itself.

地址的值不是很重要鸽捻,只是演示了上面討論的從類到 meta-class 到 NSObject 的 meta-class 的過程御蒲。

結(jié)論

meta-class 是 Class 對象的類诊赊。每個 Class 都有個不同的自己的 meta-class(因此每個 Class 都可以有一個自己不同的方法列表)。也就是說每個類的 Class 不完全相同碘箍。

meta-class 總是會保證 Class 對象會有從基類繼承的所有的的實例和類方法,加上之后繼承的類方法团搞。如從 NSObject 繼承的類多艇,就意味著在所有的 Class(和 meta-class)對象中定義了所有從 NSObject 繼承的實例和協(xié)議方法峻黍。

所有的 meta-class 使用基類的 meta-class(NSObject 的 meta-class 用于繼承自 NSObject 的類)作為他們自己的類拨匆,包括在運行時自己定義的基礎(chǔ)的 meta-class。

原文出處: cocoawithlove

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骨饿,一起剝皮案震驚了整個濱河市宏赘,隨后出現(xiàn)的幾起案子察署,更是在濱河造成了極大的恐慌贴汪,老刑警劉巖扳埂,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阳懂,死亡現(xiàn)場離奇詭異希太,居然都是意外死亡酝蜒,警方通過查閱死者的電腦和手機亡脑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門蛙紫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人僵驰,你說我怎么就攤上這事蒜茴〗鳎” “怎么了粉私?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長近零。 經(jīng)常有香客問我诺核,道長,這世上最難降的妖魔是什么久信? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任窖杀,我火速辦了婚禮,結(jié)果婚禮上裙士,老公的妹妹穿的比我還像新娘陈瘦。我一直安慰自己,他們只是感情好潮售,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布痊项。 她就那樣靜靜地躺著,像睡著了一般鞍泉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上托修,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天涩拙,我揣著相機與錄音工育,去河邊找鬼。 笑死竭沫,一個胖子當著我的面吹牛森书,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播台谍,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼掷伙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了宙地?” 一聲冷哼從身側(cè)響起电谣,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤况鸣,失蹤者是張志新(化名)和其女友劉穎臭增,沒想到半個月后誊抛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞎领,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡驼修,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年觅丰,在試婚紗的時候發(fā)現(xiàn)自己被綠了咬荷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖扮惦,靈堂內(nèi)的尸體忽然破棺而出客峭,到底是詐尸還是另有隱情舔琅,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布虚循,位于F島的核電站茎刚,受9級特大地震影響初狰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜关顷,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留隶校,地道東北人轻庆。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容