美文网首页
一、类和元类(class and metaclass)

一、类和元类(class and metaclass)

作者: LNG61 | 来源:发表于2018-05-02 19:47 被阅读0次
图一

先看一张图,描述的是关于父类和isa指针的指向。再介绍元类之前,首先要清楚几个概念:

  1. 每个对象都是具体一个类的实例对象,对象的isa指向它的类。比如说创建一个NSObject *instance = [NSObject new],这个instance对象就是类NSObject的实例,对象instanceisa指针指向类NSObject
  2. 类里面存储了一些数据,包括分配的大小、成员变量和成员方法等等。
  3. 调用对象的函数(发送消息)时,obj_msgSend从该对象中的类中查找并决定调用哪个,如果找不到则从父类中查找。比如[instance copy]会从类NSObject查找方法copy。有人会问,为什么不从instance本身去找这个copy方法?想象一下,如果每生成一个实例都会将所有的方法实现拷贝过去,那将会占用很大的内存,所以类生成实例的时候将实例的isa指向自己,调用函数时在isa指向的类中去执行该调用哪个方法的逻辑。

由此可知,类NSObject保存了所有实例方法,以供NSObject的实例调用。

介绍完类与实例的关系后,回到本文的主题,元类是什么,跟类又是什么关系?之前我们说过,每个对象都是某个类的实例对象,那类NSObject作为一个对象,那也有它对应的一个类,这个类我们通常叫做元类。元类,我们编码过程中通常不会直接用到,但确实是存在的。跟介绍实例和类的关系一样,我们也从以下几点也弄清类(class)和元类(metaclass)的关系:

  1. classmetaclass的一个实例,classisa指向metaclass,但我们通常不会直接用到metaclass
  2. metaclass里面也和class一样,存储了一些变量和方法数据。
  3. class发送消息时([NSObject alloc]),会从metaclass中找到方法alloc调用,由此可知,metaclass里面存储的是类方法。

接下来通过代码验证一下元类跟类的关系:

void classInfoFunction(id self, SEL _cmd){
  NSLog(@"This class is %p\nclass:%@\nsuperClass:%@\n", self, [self class], [self superclass]);
  Class currentClass = [self class];
  int i = 0;
  while(i++ < 4){
    NSLog(@"currentClass:%p", currentClass);
    currentClass = object_getClass(currentClass);
  }  
}

NSLog(@"NSObject class:%p metaclass:%p", [NSObject class], object_getClass([NSObject class]));

// 通过runtime注册一个NSObject的子类并添加方法classInfoFunction
Class subClass = objc_allocateClassPair([NSObject class], "testSubClass", 0);
class_addMethod(subClass, @selector(classInfo), (IMP)classInfoFunction, "v@:");
objc_registerClassPair(subClass);

id subClassInstance = [subClass new];
[subClassInstance performSelector:@selector(classInfo)];

// 以下是打印的信息
NSObject class:0x10072e148 metaclass:0x10072e0f8
This class is 0x102b00680
class:testSubClass
superClass:NSObject
currentClass:0x102b00650
currentClass:0x102b00c80 
currentClass:0x10072e0f8
currentClass:0x10072e0f8
isa指向.jpg

从上面的代码中我们可以得出上图,跟图一的规则保持一致。

注册类源码分析:
接下来我们看一下objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes),我们先简化一下该函数:

Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes){
  Class cls = alloc_class_for_subclass(superclass, extraBytes);
  Class meta = alloc_class_for_subclass(superclass, extraBytes);
  objc_initializeClassPair_internal(superclass, name,cls,meta);
  return cls;
}

objc_allocateClassPair分配了两个类clsmeta,并只将cls作为新生成的类返回,这也说明了metaclass对于我们来说是隐藏的,平时不需要用到。那么classmetaclass又是如何关联起来的呢?接下来看一下简化的函数objc_initializeClassPair_internal

static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta){
    // 初始化cls和meta的大小和数据
    cls->setData();
    meta->setData();

    cls->data()->flags = 0; // 普通类
    meta->data()->flags = RO_META; // 标记为meta

    cls->initClassIsa(meta); // 将cls的isa指向meta,这就是我们说的第1点
    if(superclass){
        // 有父类
        meta->initClassIsa(superclass->ISA()->ISA()); // meta的isa指向根类的meta
        cls->superclass = superclass;
        meta->superclass = superclass->ISA(); // meta的父类为父类的meta
    }else{
        // 作为根类
        meta->initClassIsa(meta); // 根类的isa指向自己
        cls->superclass = Nil;
        meta->superclass = cls; // 根类的父类指向cls
    }
}

通过简化后,我们可以清晰地看出,函数objc_initializeClassPair_internal主要做了这几件事:

  1. 初始化clsmeta的数据,并将metaflags标记为RO_META
  2. 根据是否为根类分别初始化clsmetaisasuperclass

总结:
元类中保存了类的实例方法的实现,父类为对应类的父类的元类,isa指针指向根类的元类。

参考文档:
1.Classes and metaclasses
2.objc_explain_Classes_and_metaclasses

相关文章

网友评论

      本文标题:一、类和元类(class and metaclass)

      本文链接:https://www.haomeiwen.com/subject/gnnylftx.html