类的底层探究(下)

一、objc_class 的内部结构

 

 

 

详情见:https://juejin.cn/post/7090843696953819172/#heading-0 

二、ro ,rw ,rwe三个结构体的理解

1.class_ro_t

class_ro_t是在编译的时候生成的。当类在编译的时候,类的属性,实例方法,协议、成员变量这些内容就存 在class_ro_t这个结构体里面了,这是一块纯净的内存空间,不允许被修改。

2.class_rw_t

class_rw_t是在运行的时候生成的,类一经使用就会变成class_rw_t,它会先将class_ro_t的内容"拿"过去,然后再将当前类的分类的这些属性、方法、协议(成员变量不会拿过去)等拷⻉到class_rw_t里面。它是可读写的.

生成时机:

在编译期间,class_ro_t结构体就已经确定,objc_class中的bits的data部分存放着该结构体的地址。在runtime运行之后,具体说来是在运行runtimerealizeClass方法时,会生成class_rw_t结构体,该结构体包含了class_ro_t,并且更新data部分,换成class_rw_t结构体的地址。

3.class_rw_ext_t

class_rw_ext_t可以减少内存的消耗。苹果在wwdc2020里面说过,只有大约10%左右的类需要动态修改。所以只有10%左右的类里面需要生成class_rw_ext_t这个结构体。这样的话,可以节约很 大一部分内存。

生成条件:

第一:   用过runtime的Api进行动态修改的时候。

第二:   有分类的时候,且分类和本类都为非懒加载类的时候。实现了+load方法即为非懒加载类。

三、苹果设计元类的目的

主要的目的是为了复用消息机制,其实在苹果的底层,压根没有区分类方法和实例方法,他们在底层实现上其实都是函数。

  • 在OC中调用方法,其实是在给某个对象发送某条消息。 消息的发送在编译的时候编译器就会把方法转换为objc_msgSend这个函数。id objc_msgSend(id self, SEL op, ...) 这个函数有俩个隐式的参数:消息的接收者,消息的方法 名。通过这俩个参数就能去找到对应方法的实现。
  • objc_msgSend函数就会通过第一个参数消息的接收者的isa指针,找到对应的类,如果我们是通过 实例对象调用方法,那么这个isa指针就会找到实例对象的类对象,如果是类对象,就会找到类对象的元类对象,然后再通过SEL方法名找到对应的imp,然后就能找到方法对应的实现
  • 那如果没有元类的话,那这个objc_msgSend方法还得多加俩个参数,一个参数用来判断这个方法 到底是类方法还是实例方法。一个参数用来判断消息的接受者到底是类对象还是实例对象。
  • 消息的发送,越快越好。那如果没有元类,在objc_msgSend内部就会有有很多的判断,就会影响 消息的发送效率。
  • 元类的出现就解决了这个问题,让各类各司其职,实例对象就干存储属性值的事,类对象存储 实例方法列表,元类对象存储类方法列表,符合设计原则中的单一职责,而且忽略了对对象类型的 判断和方法类型的判断可以大大的提升消息发送的效率,并且在不同种类的方法走的都是同一套流 程,在之后的维护上也大大节约了成本。
  • 所以这个元类的出现,最大的好处就是能够复用消息传递这套机制。不管你是什么类型的方法,都是同一套流程。

四、Runtime的API读取方法属性等

//获取类的成员变量
-(void)lg_class_copyIvarList:(Class)pClass {
    unsigned int  outCount = 0;
    Ivar *ivars = class_copyIvarList(pClass, &outCount);
    for (int i = 0; i < outCount; i ++) {
        Ivar ivar = ivars[i];
        //获取方法名字
        const char *cName =  ivar_getName(ivar);
        //获取方法类型
        const char *cType = ivar_getTypeEncoding(ivar);
        NSLog(@"name = %s type = %s",cName,cType);
    }
    free(ivars);
}

//获取类的属性
-(void)lg_class_copyPropertyList:(Class)pClass {
    unsigned int outCount = 0;
    objc_property_t *perperties = class_copyPropertyList(pClass, &outCount);
    for (int i = 0; i < outCount; i++) {
        objc_property_t property = perperties[i];
        //获取属性名字
        const char *cName = property_getName(property);
        //获取属性类型
        const char *cType = property_getAttributes(property);
        NSLog(@"name = %s type = %s",cName,cType);
    }
    free(perperties);
}

//获取类的方法
-(void)lg_class_copyMethodList:(Class)pClass {
    unsigned int outCount = 0;
    Method *methods = class_copyMethodList(pClass, &outCount);
    for (int i = 0; i < outCount; i++) {
        Method method = methods[i];
        NSString *name = NSStringFromSelector(method_getName(method));
        const char *cType = method_getTypeEncoding(method);
        NSLog(@"name = %@ type = %s",name,cType);
    }
    free(methods);
}
//类对象中获取到实例方法
-(void)methodTest:(Class)pClass {
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(instanceMethod));
    Method method2 = class_getInstanceMethod(metaClass, @selector(instanceMethod));
    Method method3 = class_getInstanceMethod(pClass, @selector(classMethod));
    //元类中获取方法的函数一样,说明元类中的方法也是实例方法,在OC底层压根没有区分元类和类,说明元类创造的目的不是为了存放类方法
    Method method4 = class_getInstanceMethod(metaClass, @selector(classMethod));
    NSLog(@"%p - %p - %p - %p",method1,method2,method3,method4);
}

-(void)impTest:(Class)pClass {
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    IMP imp1 = class_getMethodImplementation(pClass, @selector(instanceMethod));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(instanceMethod));
    IMP imp3 = class_getMethodImplementation(pClass, @selector(classMethod));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(classMethod));
    NSLog(@"%p - %p - %p - %p",imp1,imp2,imp3,imp4);
    //isa---
    
    //isa -- 
}

    [self lg_class_copyIvarList:LGPerson.class];
    //获取类的属性
    [self lg_class_copyPropertyList:LGPerson.class];
    //获取类的方法
    [self lg_class_copyMethodList:LGPerson.class];
    
    [self methodTest:LGPerson.class];
    
    [self impTest:LGPerson.class];

运行结果如下:

 五、做个小补充(其实在之前的(上)中已经写了成员变量以及类方法的存储位置)

  • 成员变量存放在类对象的class_ro_t结构体当中
  • 类方法存在元类当中

 

 

 

 

posted on 2022-04-28 21:57  suanningmeng98  阅读(128)  评论(0编辑  收藏  举报