OC中的对象、类与元类的关系

时间:2021-7-3 作者:qvyue

先以运行时动态创建一个class为引,通过剖析创建的class pair来弄明白到底meta-class是什么以及更深入的了解它对于OC中对象、类的意义。

1. 在运行时创建一个类

在运行时创建一个NSError的子类:

Class newClass = 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 

在运行时创建一个类需要三步:
1.使用objc_allocateClassPair为”class pair”分配空间;
2.使用class_addMethod为类添加方法和成员;
3.使用objc_registerClassPair注册你创建的这个类,使其可用。

可见objc_allocateClassPair只返回一个值:Class。“class pair”即这个成对出现的结构关系,对于”class pair”的另一半就是指meta-class

2. 一个数据结构何以称为对象

每个对象都会有一个它所属的类,这是面向对象的基本概念。在OC中,这对所有数据结构有效,只要在数据结构恰当的位置有一个指针指向一个class,那么,它都可以被认为是一个对象。在OC中,一个对象所属于哪个类,是由它的isa指针指向的。这个isa指针指向这个对象所属的class。

实际上,OC中对象的定义是如下的样子:

typedef struct objc_object {      
  Class isa;
}*id;

即,任何以一个指向Class的指针作为首个成员的数据结构都可以被认为是一个objc_object。

最重要的特性就是,你可以向OC中的任何对象发送消息,如下这样:

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

运行原理就是,当你向一个OC对象发送消息时(此处为@”stringValue”),运行时系统会根据对象的isa指针找到这个对象所属的类,这个类会包含一个所有实例方法的列表及一个指向superclass的指针以便可以找到父类的实例方法。运行时库会在类的方法列表以及向上父类的方法列表中寻找符合这个selector(此处selector为”writeToFile:atomically:encoding:error”)的方法。找到后即运行这个方法。

3. meta-class 的本质

由上述可知一个OC的类也是一个对象,意思就是你可以向一个类发送消息。

NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];

其中,defaultStringEncoding 被发送给了NSString类,也就是说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是一个类对象的类。
1.当你向一个对象发送消息时,runtime会在这个对象所属的那个类的方法列表中查找。
2.当你向一个类发送消息时,runtime会在这个类的meta-class的方法列表中查找。
meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。

4. 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指针指向它自己。(译:完美的闭环)

就像一个类使用super_class指针指向自己的父类一样,meta-class的super_class会指向类的super_class的meta-class。一直追溯到基类的meta-class,它的super_class会指向基类自身。(译:万物归根)

这样一来,整个继承体系中的实例、类和meta-class都派生自继承体系中的基类。对于NSObject继承体系来说,NSObject的实例方法对体系中所有的实例、类和meta-class都是有效的;NSObject的类方法对于体系中所有的类和meta-class都是有效的。

OC中的对象、类与元类的关系
实例、类与元类的关系

执行例中ErportFunction方法可以证实以上论述:

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

以下是程序的输出:

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

这些地址的值并不重要,重要的是它们说明了从实例、class到meta-class到NSObject的meta-class的整个流程。

总结:
1.meta-class是类对象的类,每个类都有自己单独的meta-class;
2.对于所有NSObject继承体系下的类,NSObject的实例方法和协议方法对他们和他们meta-class的对象都要有效。
3.所有的meta-class使用基类的meta-class作为自己的基类,对于顶层基类的meta-class也是一样,只是它指向自己而已。

参考链接:
https://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。