美文网首页
一、类和元类(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