作者: 雷霸龙 | 来源:发表于2021-04-01 23:05 被阅读0次

1、元类

  • 我们都知道对象的isa是指向类,类其实也是一个对象,可以称为类对象,其isa的位域指向苹果定义的元类
  • 元类是系统给的,其定义和创建都是由编译器完成,在这个过程中,类的归属来自于元类
  • 元类是类对象的类,每个类都有一个独一无二的元类用来存储类方法的相关信息。
  • 元类本身是没有名称的,由于与类相关联,所以使用了同类名一样的名称

关系链:对象 --> 类 --> 元类 --> NSobject,NSObject指向自身

2、NSObject到底有几个?

最后NSObject类的元类也是NSObject,内存中只存在一份根元类NSObject,根元类的元类是指向它自己

[面试题]:类存在几份?

由于类的信息在内存中永远只存在一份,所以类对象只有一份

3、著名的isa走位和继承关系图

image.png
isa走位
  • 实例对象(Instance of Subclass)的isa指向类(class)
  • 类对象(class)isa指向元类(Meta class)
  • 元类(Meta class)的isa指向根元类(Root metal class)
  • 根元类(Root metal class)的isa指向它自己本身,形成闭环,这里的根元类就是NSObject
superclass走位(即继承关系)

类之间的继承关系:

  • 类(subClass)继承自父类(superClass)
  • 父类(superClass)继承自根类(RootClass),此时的根类是指NSObject
  • 根类继承自nil,所以根类即NSObject可以理解为万物起源,即无中生有

元类也存在继承,元类之间的继承关系如下:

  • 子类的元类(metal SubClass)继承自父类的元类(metal SuperClass)
  • 父类的元类(metal SuperClass)继承自根元类(Root metal Class
  • 根元类(Root metal Class)继承于根类(Root class),此时的根类是指NSObject

注意:实例对象之间没有继承关系,类之间有继承关系

4、objc_class & objc_object

为什么对象和类都有isa属性呢?这里就不得不提到两个结构体类型:objc_class & objc_object

NSObject的底层编译是NSObject_IMPL结构体,其中 Class是isa指针的类型,是由objc_class定义的类型,而objc_class是一个结构体。在iOS中,所有的Class都是以 objc_class 为模板创建的

struct NSObject_IMPL {
    Class isa;
};


typedef struct objc_class *Class;
struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};
objc_class 与 objc_object 有什么关系?
  • 结构体类型objc_class继承自objc_object类型,其中objc_object也是一个结构体,且有一个isa属性,所以objc_class也拥有了isa属性
  • mian.cpp底层编译文件中,NSObject中的isa在底层是由Class定义的,其中class的底层编码来自objc_class类型,所以NSObject也拥有了isa属性
  • NSObject是一个类,用它初始化一个实例对象objc,objc满足objc_object的特性(即有isa属性),主要是因为isa是由NSObject从objc_class继承过来的,而objc_class继承自objc_object,objc_object有isa属性。所以对象都有一个isa,isa表示指向,来自于当前的objc_object
  • objc_object(结构体)是当前的根对象,所有的对象都有这样一个特性objc_object,即拥有isa属性
objc_object 与对象的关系
  • 所有的对象都是以objc_object为模板继承过来的
  • 所有的对象是来自NSObject(OC),但是真正到底层的是一个objc_object(C/C++)的结构体类型

5、方法列表存储位置

  • 类的实例方法存储在类的bits属性中,通过bits --> methods() --> list获取实例方法列表,例如CJLPersong类的实例方法sayHello就存储在CJLPerson类的bits属性中,类中的方法列表除了包括实例方法,还包括属性的set方法和get方法
  • 类的类方法存储在元类的bits属性中,通过元类bits --> methods() --> list获取类方法列表,例如CJLPerson中的类方法sayBye就存储在CJLPerson类的元类(名称也是CJLPerson)的bits属性中

6、什么是属性、成员变量、实例变量?

  • 属性(property):在OC中是通过@property开头定义,且是带下划线成员变量 + setter + getter方法的变量
  • 成员变量(ivar):在OC的类中{}中定义的,且没有下划线的变量
  • 实例变量:通过当前对象类型,具备实例化的变量,是一种特殊的成员变量,例如NSObject、UILabel、UIButton等

7、元类中为什么会有类对象的类方法?

lgObjc_copyMethodList函数分析

在这个函数中,主要是获取类中的方法列表,实例方法存储在类中,类方法存储在元类中

lgInstanceMethod_classToMetaclass函数分析

在分析前,需要先了解class_getInstanceMethod这个方法,主要是用于获取实例方法,如果在传入的类或者类的父类中没有找到指定的实例方法,则返回NULL

lgClassMethod_classToMetaclass函数分析

在分析前,需要先了解class_getClassMethod这个方法,主要是用于获取类方法,如果在传入的类或者类的父类中没有找到指定的类方法,则返回NULL

//获取类方法
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

//获取元类
 // NOT identical to this->ISA when this is a metaclass 判断是否是元类,是元类就直接返回,反之,继续找isa指向
Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

看该方法的源码实现,可以得出class_getClassMethod的实现是获取类的类方法,其本质就是获取元类的实例方法,最终还是会走到class_getInstanceMethod,但是在这里需要注意的一点是:在getMeta源码中,如果判断出cls是元类,那么就不会再继续往下递归查找,会直接返回this,其目的是为了防止元类的无限递归查找

源码流程图如下所示

image.png
lgIMP_classToMetaclass函数分析

class_getMethodImplementation 主要是返回方法的具体实现

IMP class_getMethodImplementation(Class cls, SEL sel)
{
    IMP imp;

    if (!cls  ||  !sel) return nil;

    //查找方法实现
    imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);

    //如果没有找到,则进行消息转发
    if (!imp) {
        return _objc_msgForward;
    }

    return imp;
}

该函数在向类实例发送消息时会被调用,并返回一个指向方法实现函数的指针。这个函数会比method_getImplementation(class_getInstanceMethod(cls, name))更快。返回的函数指针可能是一个指向runtime内部的函数,而不一定是方法的实际实现。如果类实例无法响应selector,则返回的函数指针将是运行时消息转发机制的一部分

总结:
  • class_getInstanceMethod:获取实例方法,如果指定的类或其父类不包含带有指定选择器的实例方法,则为NULL
  • class_getClassMethod:获取类方法,如果指定的类或其父类不包含具有指定选择器的类方法,则为NULL。
  • class_getMethodImplementation:获取方法的具体实现,如果未查找到,则进行消息转发

相关文章

网友评论

      本文标题:

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