詳解Objective-C的meta-class

轉載自:http://blog.csdn.net/windyitian/article/details/19810875
比較簡單的一篇英文,重點是講解meta-class。翻譯下,加深理解。
原文標題:What is a meta-class in Objective-C?
原文地址:http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.htmlhtml

本篇將會探討一個在Objective-C中相對陌生的概念 – meta-class。OC中的每個類都會有一個與之相關聯的meta class,可是你卻幾乎永遠也不會直接使用到,它們始終籠罩着一層神祕的面紗。筆者將以運行時動態建立一個class爲引,經過剖析建立的class pair來弄明白到底meta-class是什麼以及更深刻的瞭解它對於OC中對象、類的意義。objective-c

在運行時建立類
如下代碼演示運行時建立一個NSError的子類,同時添加一個實例方法給它:ruby

Class newClass =  
    objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0);  
class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");  
objc_registerClassPair(newClass);

函數ReportFunction就是添加的實例方法的具體實現,以下:markdown

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).
二、爲建立的類添加方法和成員(上例使用class_addMethod添加了一個方法)。
三、註冊你建立的這個類,使其可用(使用objc_registerClassPair)。數據結構

估計讀者立刻就要問:什麼是「class pair」? objc_allocateClassPair只返回一個值:Class。那麼pair的另外一半在哪裏呢?
是的,估計你已經猜到了這個另外一半就是meta-class,也就是這篇短文的標題,可是要解釋清楚它是什麼,爲何須要它,還須要交代下OC的對象與類的相關背景。app

一個數據結構何以成爲一個對象?函數

每一個對象都會有一個它所屬的類。這是面向對象的基本概念,可是在OC中,這對全部數據結構有效。任何數據結構,只要在恰當的位置具備一個指針指向一個class,那麼,它均可以被認爲是一個對象。
在OC中,一個對象所屬於哪一個類,是由它的isa指針指向的。這個isa指針指向這個對象所屬的class。
實際上,OC中對象的定義是以下的樣子:ui

typedef struct objc_object {  
      Class isa;  
}*id;

這個定義代表:任何以一個指向Class的指針做爲首個成員的數據結構均可以被認爲是一個objc_object.
最重要的特性就是,你能夠向OC中的任何對象發送消息,以下這樣:atom

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

運行原理就是,當你向一個OC對象發送消息時(上文的@「stringValue」),運行時庫會根據對象的isa指針找到這個對象所屬的類(上文爲例,會找到NSCFString類).這個類會包含一個全部實例方法的列表及一個指向superclass的指針以即可以找到父類的實例方法。運行時庫會在類的方法列表以及父類(們)的方法列表中尋找符合這個selector(上文爲例,這個selector是」writeToFile:atomically:encoding:error」)的方法。找到後即運行這個方法。關鍵點就是類要定義這個你發送給對象的消息。spa

什麼是meta-class?

至此,你可能已經知道,一個OC的類其實也是一個對象,意思就是你能夠向一個類發送消息。
NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];
在這個例子中,defaultStringEncoding 被髮送給了NSString類。由於每個OC的類自己也是一個對象。也就是說Class的數據結構必然也是以isa指針開始的在二進制級別上與objc_object是徹底兼容的。而後一個類結構的下一個字段必定是一個指向super class的指針(或者指向nil,對於基類而言)。
一個類如何定義有不少方法,依賴於你的運行時庫版本,可是無論哪一種方法,他們都是以一個isa做爲第一個字段,接着是superclass字段。

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

爲了能夠調用類方法,這個類的isa指針必須指向一個包含這些類方法的類結構體。
這樣就引出了meta-class的概念:meta-class是一個類對象的類。
簡單解釋下:
當你向一個對象發送消息時,runtime會在這個對象所屬的那個類的方法列表中查找。
當你向一個類發送消息時,runtime會在這個類的meta-class的方法列表中查找。
meta-class之因此重要,是由於它存儲着一個類的全部類方法。每一個類都會有一個單獨的meta-class,由於每一個類的類方法基本不可能徹底相同。

meta-class的類又是什麼呢?

meta-class,就像Class同樣,也是一個對象。你依舊能夠向它發送消息調用函數,天然的,meta-class也會有一個isa指針指向其所屬類。全部的meta-class使用基類的meta-class做爲他們的所屬類。具體而言,任何NSObject繼承體系下的meta-class都使用NSObject的meta-class做爲本身所屬的類。
根據這個規則,全部的meta-class使用基類的meta-class做爲它們的類,而基類的meta-class也是屬於它本身,也就是說基類的meta-class的isa指針指向它本身。(譯:完美的閉環)

類和meta-class的繼承

就像一個類使用super_class指針指向本身的父類同樣,meta-class的super_class會指向類的super_class的meta-class。一直追溯到基類的meta-class,它的super_class會指向基類自身。(譯:萬物歸根)
這樣一來,整個繼承體系中的實例、類和meta-class都派生自繼承體系中的基類。對於NSObject繼承體系來講,NSObject的實例方法對體系中全部的實例、類和meta-class都是有效的;NSObject的類方法對於體系中全部的類和meta-class都是有效的。
用文字描述總會讓人迷糊,Greg Parker給出了一份精彩的圖譜來展現這些關係:
這裏寫圖片描述

注意:全部metaclass中isa指針都指向根metaclass。而根metaclass則指向自身。Root metaclass是經過繼承Root class產生的。與root class結構體成員一致,也就是前面提到的結構。不一樣的是Root metaclass的isa指針指向自身。

實驗證實:

爲了證明以上的論述,讓咱們查看下開篇代碼中ReportFunction的輸出。這個函數的目的就是沿着isa指針進行打印。
爲了運行ErportFunction,咱們須要建立一個實例,並調用report方法。

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

由於咱們並無對report方法進行聲明,因此咱們使用performSelector進行調用,這樣避免編譯器警告。
而後ReportFunction函數會沿着isa進行檢索,來告訴咱們class,meta-class以及meta-class的class是什麼樣的狀況:

【注:ReportFunction使用object_getClass來獲取isa指針指向的類,由於isa指針是一個受保護成員,你不能直接訪問其餘對象的isa指針。ReportFunction沒有使用class方法是由於在一個類對象上調用這個方法是沒法得到meta-class的,它只是返回這個類而已。(因此[NSString class]只是返回NSString類,而不是NSString的meta-class]
如下是程序的輸出:

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

觀察經過isa得到的地址:
對象的地址是 0x10010c810.
類的地址是 0x10010c600.
類的meta-class地址是 0x10010c630.
類的meta-class的類地址是 0x7fff71038480.(即NSOjbect的meta-class)
NSObject的meta-class的類地址是它自身。
這些地址的值並不重要,重要的是它們說明了文中討論的從類到meta-class到NSObject的meta-class的整個流程。

結論:

meta-class是類對象的類,每一個類都有本身單獨的meta-class。全部的類對象並不會屬於同一個meta-class。 meta-class要保證類對象具備繼承體系中基類的全部實例和類方法,以及繼承體系中的全部中間類方法。對於全部NSObject繼承體系下的類,NSObject的實例方法和協議方法對他們和他們meta-class的對象都要有效。 全部的meta-class使用基類的meta-class做爲本身的基類,對於頂層基類的meta-class也是同樣,只是它指向本身而已。

相關文章
相關標籤/搜索