详解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也是同样,只是它指向本身而已。

相关文章
相关标签/搜索