La couche inférieure de la classe de principe sous-jacent OC (04) (on)

1. 对象La relation isaentre

Grâce à la recherche sur la nature des objets OC et isa , nous avons constaté que chaque objet a des variables membres isa, et aujourd'hui nous l'utilisons comme entrée pour étudier 对象la relation isaentre et.

Pas grand chose à dire, sur le code, créez d'abord deux classes YJPerson(继承NSObject), YJTeacher(继承YJPerson), pour les tests ; comme suit :

// YJPerson
@interface YJPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end

// YJTeacher
@interface YJTeacher : YJPerson
@property (nonatomic, copy) NSString *subject;
@end

1.1 est une position de course

/// isa 链路
void testIsaChain (id obj)
{
    // 对象所属类 - 对象的isa指向
    Class cls = object_getClass(obj);
    // 元类 - 类的isa指向
    Class metaCls = object_getClass(cls);
    // 根元类 - 元类的isa指向
    Class rootCls = object_getClass(metaCls);
    // 根根元类 - 根元类的isa指向
    Class metaOfRootCls = object_getClass(rootCls);

    NSLog(@"对象的      isa -> : %@", obj);
    NSLog(@"类的的      isa -> : %@, %p", cls, cls);
    NSLog(@"元类的      isa -> : %@, %p", metaCls, metaCls);
    NSLog(@"根元类的    isa -> : %@, %p", rootCls, rootCls);
    NSLog(@"根根元类的   isa -> : %@, %p\n", metaOfRootCls, metaOfRootCls);
}

in main(int argc, const char * argv[]) {
    @autoreleasepool {
        // isa 链路测试
        testIsaChain(NSObject.new);
        testIsaChain(YJPerson.new);
        testIsaChain(YJTeacher.new);
        NSLog(@"Hello, World!");
    }
    return 0;
}

résultat d'impression :

1656660086668.jpg

En imprimant les résultats, on conclut que :

对象 -isa-> 类 -isa-> 元类 -isa-> 根元类 -isa-> 根元类自己

1.2 bitmap superclasse

/// super class 链路
void testSuperClassChain (Class cls)
{
    Class superCls = class_getSuperclass(cls);
    NSLog(@"父类 : %@, %p", superCls, superCls);

    if (!superCls) {
        NSLog(@"\n");
        return;
    }
    testSuperClassChain(superCls);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 类的 superclass 链路
        Class objCls = NSObject.class;
        NSLog(@"类 : %@, %p", objCls, objCls);
        testSuperClassChain(objCls);

        Class personsCls = YJPerson.class;
        NSLog(@"类 : %@, %p", personsCls, personsCls);
        testSuperClassChain(personsCls);

        Class teacherCls = YJTeacher.class;
        NSLog(@"类 : %@, %p", teacherCls, teacherCls);
        testSuperClassChain(teacherCls);

        // 原类的 superclass 链路
        Class objMetaClass = object_getClass(objCls);
        NSLog(@"%@ 的元类 : %@, %p", objCls, objMetaClass, objMetaClass);
        testSuperClassChain(objMetaClass);

        Class personMetaClass = object_getClass(personsCls);
        NSLog(@"%@ 的元类 : %@, %p", personsCls, personMetaClass, personMetaClass);
        testSuperClassChain(personMetaClass);

        Class teacherMetaClass = object_getClass(teacherCls);
        NSLog(@"%@ 的元类 : %@, %p", teacherCls, teacherMetaClass, teacherMetaClass);
        testSuperClassChain(teacherMetaClass);
    }
    return 0;
}

Résultat de sortie :

1656663474113.jpg

Grâce à l'analyse, nous pouvons constater superclass 链路qu'il peut être divisé en deux catégories:

  1. 类的lien superclasse :

    -supercls-> 父类-supercls-> ...-supercls-> NSObject类-supercls->nil

  2. 类的元类lien superclasse :

    类的元类-supercls-> 父类的元类-supercls-> ...-supercls-> NSObject的元类-supercls-> NSObject类-supercls->nil

Enfin, pointez sur l'organigramme :

isa organigramme.png

Jusqu'à présent, j'ai une compréhension claire de la relation de lien avec et de la 对象relation de chaîne d'héritage avec .isasuperclass

Cependant, à quoi ressemble la structure interne des membres, nous ne le savons toujours pas. Continuez à explorer. . .

2. Analyse structurelle des classes

Basé sur objc4 (version 838)objc_class 源码

struct objc_class : objc_object {
    // 省略 ...
    
    // Class ISA; // isa 继承自 objc_object 8字节  
    Class superclass; // 8字节
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    // 省略 ...
    
    class_rw_t *data() const {
        return bits.data();
    }
    
    // 省略 ...
}
  • isaMembre : hérité de objc_object, isaoccupe des 8octets

  • superclass 成员:Class类型,Classtypedef struct objc_class *Class;定义的,是一个指针,占8字节

  • cache 成员:简单从类型class_data_bits_t目前无法得知,而class_data_bits_t是一个结构体类型,结构体内存大小需要根据内部的成员来确定

  • bits 成员:只有首地址经过上面3个属性的内存大小总和的平移,才能获取到bits

2.1 计算 cache 的内存大小

cache_t 的定义:

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
// 省略无关代码。。。

cache_t 分析:

cache_t 有两个成员:_bucketsAndMaybeMask一个联合体

  • _bucketsAndMaybeMaskuintptr_t无符长整型占8字节

  • 联合体里面有两个成员变量结构体_originalPreoptCache(联合体的内存大小由成员变量中的最大成员变量的类型决定)

    • _originalPreoptCache 是个指针占8字节

    • 结构体 中有 _maybeMask_flags_occupied

      • _maybeMaskuint32_t4字节
      • _flagsuint16_t 各占2字节
      • _occupieduint16_t 各占2字节
      • 结构体 大小是 4 + 2 + 2 = 8字节
  • cache_t的内存大小是 8 + 8 = 16 字节

2.2 获取 bits

我们已经知道了类结构中 isa(8字节)superclass(8字节)cache(16字节) 的大小,接下来只需要通过地址偏移8 + 8 + 16 = 32,即可得到 bits 的地址

Xnip2022-07-03_21-59-13.png

  • 其中的data()获取数据,是由objc_class提供的方法

Xnip2022-07-03_22-02-41.png

  • 从源码 或 lldb调试中都可以看出 bits.data() 中存储的信息,其类型是class_rw_t,也是一个结构体类型。但我们还是没有看到属性列表、方法列表等,需要继续往下探索

2.3 探索 属性列表,即 property_list

通过查看class_rw_t定义的源码发现,结构体中有提供相应的方法去获取 属性列表、方法列表等,如下所示:

Xnip2022-07-03_22-10-43.png

获取bits并打印bits信息的基础上,通过class_rw_t提供的方法,继续探索 bits中的属性列表,以下是lldb 探索的过程图示:

Xnip2022-07-03_23-08-02.png

  • p $2.properties()命令中的propertoes方法是由class_rw_t提供的,返回值类型是 property_array_t

  • list 的类型是 property_list_t,是一个指针,所以通过 p *$5获取内存中的信息,同时也证明 bits中存储了 property_list,即属性列表

2.4 探索 方法列表,即 methods_list

在前文中提到的 YJPerson 中添加两个方法:实例方法sayHello,类方法 eat

// YJPerson.h
@interface YJPerson : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
- (void)sayHello;
+ (void)eat;
@end

// YJPerson.m
@implementation YJPerson
- (void)sayHello { }
+ (void)eat { }
@end
2.4.1 方法的获取

参照属性列表的获取,我们来获取方法列表,如下图:

Xnip2022-07-04_10-17-01.png

这里和属性获取不太一样,属性列表 property_list_t里存储的是 property_t,而 property_t 是这样的:

struct property_t {
    const char *name;
    const char *attributes;
};

所以属性通过索引可以直接输出属性的信息。方法列表method_list_t 存储的是 method_tmethod_t 是这样的:

struct method_t {
    // 省略。。。
    // 方法描述结构体
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
    // 省略。。。
    // big()方法 返回方法描述结构体
    big &big() const {
        ASSERT(!isSmall());
        return *(struct big *)this;
    }
    // 省略。。。
}

所以需要在获取 method_t 之后再调用 big() 方法,才能正常输出

2.4.2 方法分析

Xnip2022-07-04_10-24-30.png

通过打印 method_list_t 中的每个 method_t,发现并没有类方法eat;由此可知:method_list_t 存储的是类的对象方法。那么 类方法eat存在哪儿呢?不捉急,后续篇章为您揭秘。。。

总结

Il y a  des variables isa, superclass, , chcheet membres dans la classe. Au cours du  processus d'exploration, on constate que les familiers , , , etc. sont stockés, ce qui ouvre notre compréhension . Nous continuerons à explorer la classe plus tard, alors restez à l'écoutebitsbitsbits属性列表方法列表成员变量列表协议列表

Guess you like

Origin juejin.im/post/7116165537989656589