[譯]Objective-C中的meta-class是什麼。

原文地址: http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.htmlhtml

在這篇文章中,我看到了Objective-C中的一個陌生概念——元類。Objective-C中的每一個類都有它本身的相關元類,可是因爲您不多直接使用元類,因此它們仍然是謎同樣的。我將從如何在運行時建立類開始。經過檢查這個建立的「類對」,我將解釋元類是什麼,而且還涵蓋了Objective-C中常見的類與數據。objective-c

用runtime建立一個類

下面的代碼用運行時建立了NSError的一個新的子類,並添加了一個方法:bash

objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0);
class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
objc_registerClassPair(newClass); 
複製代碼

添加的方法使用名爲ReportFunction的函數做爲其實現,其定義以下:數據結構

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]));
}
複製代碼

表面上看,這一切都很簡單。在運行時建立類僅僅是三個簡單的步驟:函數

爲「class pair」分配存儲(使用objc_allocateClassPair)。 根據須要向類添加方法和ivars(我已經添加了一個使用class_addMethod的方法)。 註冊類以便它能夠被使用(使用objc_registerClassPair)。 然而,最直接的問題是:什麼是「class pair」? 函數objc_allocateClassPair只返回一個值:類。另外一半在哪裏?atom

我確信你已經猜到了,另外一半是元類(這是這個帖子的標題),可是爲了解釋這是什麼以及爲何須要它,我將介紹Objective-C中的對象和類的一些背景知識。spa

數據結構須要什麼才能成爲對象?

每一個對象都有一個類。這是一個基本的面向對象概念,但在Objective-C中,它也是數據的基礎部分。任何具備指向正確位置上的類的指針的數據結構均可以被視爲對象。3d

在Objective-C中,對象的類由它的isa指針決定。isa指針指向對象的類。指針

實際上,Objective-C中對象的基本定義是這樣的:code

typedef struct objc_object {
    Class isa;
} *id;
複製代碼

這就是說:任何從指向類結構的指針開始的結構均可以被視爲objc_object。

Objective-C中對象最重要的特性是能夠向它們發送消息:

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

這是有效的,由於當您向Objective-C對象(好比這裏的NSCFString)發送消息時,運行時將尋找對象的isa指針到達對象的類(在本例中爲NSCFString類)。而後,該類包含一個方法列表,該列表適用於該類的全部對象,以及一個指向超類的指針,以查找繼承的方法。運行時經過的關於類和超類的方法來找到一個匹配的消息選擇器(在本例中,writeToFile:atomically:encoding:error on NSString)。而後運行時調用該方法的函數(IMP)。

重要的一點是,類定義了能夠發送到對象的消息。

什麼是meta-class?

大家可能已經知道,Objective-C中的一個類也是一個對象。這意味着您能夠向類發送消息。

NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];
複製代碼

在這種狀況下,defaultStringEncoding被髮送到NSString類。

這是由於Objective-C中的每一個類都是一個對象。這意味着類結構必須從一個isa指針開始,這樣它就能夠與我上面顯示的objc_object結構相兼容,而且結構中的下一個字段必須是一個指向超類的指針(或基類的nil)。

有幾種不一樣的方法能夠定義一個類,這取決於您運行的運行時的版本,可是,它們都是從一個isa字段開始,而後是一個超類字段。

typedef struct objc_class *Class;
struct objc_class {
    Class isa;
    Class super_class;
    /* followed by runtime specific details... */
};
複製代碼

然而,爲了讓咱們在類上調用方法,類的isa指針必須指向類結構,類結構必須包含咱們能夠在類上調用的方法的列表。

這導出了元類的定義:元類是類對象的類。

簡單地說:

當向對象發送消息時,該消息將在對象類的方法列表中查找。 當您向一個類發送消息時,該消息將在類的元類的方法列表中查找。 元類很是重要,由於它存儲類的類方法。每一個類都必須有一個惟一的元類,由於每一個類都有一個可能惟一的類方法列表。

元類的類是什麼

元類,就像前面的類同樣,也是一個對象。這意味着您也能夠在它上調用方法。固然,這意味着它也必須有一個類。

全部元類都使用基類的元類(繼承層次上的頂級類的元類)做爲類。這意味着對於從NSObject(大多數類)層級的全部類,元類都是NSObject元類做爲它的類。

遵循全部元類都使用基類的元類做爲類的規則,任何基礎元類都將是它本身的類(它們的isa指針指向它們本身)。這意味着在NSObject元類上的isa指針指向它本身(它自己就是一個實例)。

在此附上一張圖幫助理解

類和元類的繼承

就像類用它的超類指針指向超類同樣,元類用它本身的超類指針指向類的超類的元類。

基類的元類將它的super_class設置爲基類自己。

這種繼承體系就是,體系中的全部實例、類和元類都繼承了體系的基類。

對於NSObject層級中的全部實例、類和元類,這意味着全部NSObject實例方法都是有效的。對於類和元類,全部NSObject類方法都是有效的。

實驗證明這一猜測

爲了確認全部這些,讓咱們看一下我在本文開頭給出的ReportFunction的輸出。這個函數的目的是跟蹤isa指針並記錄它找到的內容。

要運行ReportFunction,咱們須要動態建立類的實例並在其上轉發方法。

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

因爲沒有報告方法的聲明,因此我使用performSelector來調用它:因此編譯器不會發出警告。

ReportFunction如今將遍歷isa指針並告訴咱們什麼對象被用做類、元類和元類的類。

獲取一個對象的類:ReportFunction使用object_getClass來跟蹤isa指針,由於isa指針是類的受保護成員(您不能直接訪問其餘對象的isa指針)。ReportFunction不使用類方法來執行此操做,由於在類對象上調用類方法不會返回元類,而是返回類(所以[NSString類]將返回NSString類而不是NSString元類)。 當程序運行以後,如下是輸出


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」值達成的地址:

對象是地址0x10010c810。 這個類的地址是0x10010c600。 元類是地址0x10010c630。 元類的類(即NSObject元類)是地址0x7fff71038480。 NSObject的元類是它本身。 地址的值並不重要,只是它顯示了從類到元類到NSObject元類的。

結論

元類是一個類的類對象。每一個類都有本身的獨特的元類(由於每個類能夠有它本身的獨特的方法列表)。(This means that all Class objects are not themselves all of the same clas不會。。歡迎在討論區補充)

類對象的元類老是確保全部基類的實例和類方法的體系,加上中間全部的類方法。NSObject的子類,這個子類含有全部NSObject實例和協議方法,這些是從元類被定義好了的。

全部的元類自己,使用基類的元類( NSObject元類)做爲他們的類,包括基礎的元類在運行時中都是惟一類。

全文終 感謝閱讀 不許確的地方歡迎指正。

相關文章
相關標籤/搜索