👨 🏻 💻 making Demo

Easy to remember:

  • OC there are three types of objects: instance, class, and meta-class
    • Instance object: NSObject converted to C is actually a structure, the system allocates memory space, store a member isa pointer to represent the address of the object (the address of the structure) 64-bit 8 bytes, 32-bit 4 bytes
    • Class objects: Information stored in class object memory: ISA and Superclass Pointers, class attribute information and member variables, object method and protocol information
    • Meta-class metaclasses: Metaclasses have the same memory structure as class objects. Isa Pointers to base metaclasses point to themselves
  • OC Three object principles: Pointers to objC_class structures

Low-level implementation of OC objects

Find the nature of OC objects, we usually write objective-C code, the underlying implementation is actually C\C++ code.

OC object structures are implemented through the base C\C++ structure. We explore the nature of OC objects by creating OC files and objects and converting OC files into C++ files

OC the following code

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
        NSLog(@"Hello, World!");
    }
    return 0;
}
Copy the code

We converted OC’s mian. M file to a c++ file through the command line

Clang-rewrite-objc main.m -o main.cpp // This approach does not specify a schema such as arm64Copy the code

We can specify schema patterns on the command line using the Xcode tool XCRun

Xcrun-sdk iphoneOS clang-arch arm64-rewrite-objc main.m -o main-arm64.cpp // Generate main-arm64.cppCopy the code

In main-arm64.cpp, search for NSObjcet and find NSObjcet_IMPL (IMPL stands for implementation)

Let’s look inside the NSObject_IMPL

struct NSObject_IMPL {
    Class isa;
};
// Look at the nature of Class
typedef struct objc_class *Class;
// We can see that Class is just a pointer, and that's what the underlying implementation of an object looks like.
Copy the code

Consider: How is an OC object laid out in memory?

The underlying implementation of NSObjcet, click NSObjcet to discover the internal implementation of NSObject

@interface NSObject <NSObject> {
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
    #pragma clang diagnostic pop
}
@end
Copy the code

Converting to C is actually a structure

struct NSObject_IMPL {
    Class isa;
};
Copy the code

So how much memory does this structure take up? We find that this structure has only one member, the ISA pointer, and Pointers take up 8 bytes in a 64-bit architecture. That is, an NSObjec object takes up 8 bytes of memory.

To see how OC objects are represented in memory, let’s look at the following code

NSObject *objc = [[NSObject alloc] init];
Copy the code

How does the above code look in memory? In the preceding code, the NSObject object is allocated 8 bytes of memory to hold a member ISA pointer. The address of the isa pointer variable is the address of the structure, that is, the address of the NSObjcet object.

Assuming the address of ISA is 0x100400110, the above code allocates storage to the NSObject object and then assigns the address of storage to the objC pointer. Objc stores the address of isa. Objc points to the NSObject object address in memory, that is, to the structure in memory, the isa location.


Internal implementation of a custom class

@interface Student : NSObject{
    @public
    int _no;
    int _age;
}
@end
@implementation Student
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Student *stu = [[Student alloc] init];
        stu -> _no = 4;
        stu -> _age = 5;
        NSLog(@ "% @",stu);
    }
    return 0;
}
@end
Copy the code

Follow the above steps to generate the c++ file as well. Student, we find Student_IMPL

struct Student_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    int _no;
    int _age;
};
Copy the code

The first one found is an implementation of NSObject_IMPL. Struct NSObject_IMPL NSObject_IVARS; struct NSObject_IMPL NSObject_IVARS;

struct Student_IMPL {
    Class *isa;
    int _no;
    int _age;
};
Copy the code

So the object takes up as much storage space as the structure takes up. Isa pointer 8 bytes space +int _NO4 bytes space +int _age4 bytes space total 16 bytes space

Student *stu = [[Student alloc] init];
stu -> _no = 4;
stu -> _age = 5;
Copy the code

Creating Student allocates 16 bytes, stores 3 things, isa pointer 8 bytes, _no 4 bytes, and _age 4 bytes

The three variables of the sutdent object have their own addresses. Stu points to the address of the ISA pointer. So the address of stu is 0x100400110, and the STU object takes up 16 bytes of memory. And the assignment is that _no holds 4 and _age holds 5

Verify Student’s appearance in memory

struct Student_IMPL {
    Class isa;
    int _no;
    int _age;
};
@interface Student : NSObject {
    @public
    int _no;
    int _age;
}
@end
@implementation Student
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // Force conversion
        struct Student_IMPL *stuImpl = (__bridge struct Student_IMPL *)stu;
        NSLog(@"_no = %d, _age = %d", stuImpl->_no, stuImpl->_age); // Prints _no = 4, _age = 5
    }
    return 0;
}
Copy the code

The above code forces the OC object into a structure for Student_IMPL. So that means I’m going to point a pointer to an OC object to this structure. Since we assumed that the layout of objects in memory is the same as that of structures in memory, if the transformation can be successful, it means that our guess is correct. This shows that the memory that stu points to is indeed a structure.

To actually get the memory footprint of an object, you can use a more convenient run-time method.

class_getInstanceSize([Student class])
NSLog(@"%zd,%zd", class_getInstanceSize([NSObject class]) ,class_getInstanceSize([Student class]));
// Prints information 8 and 16
Copy the code

Snooping memory structure

View memory data in real time

Method 1: Through the interruption point. Debug Workflow -> viewMemory Address Enter the STU address

From the figure above, we can see that the read data starts from the high data. Looking at the first 16 bytes, every four bytes are read in hexadecimal 0x0000004(4 bytes) 0x0000005(4 bytes) isa address is 00D1081000001119(8 bytes).

Method 2: Use the debugger of the LLDB directive Xcode

memory read 0x10074c450
// Short x 0x10074C450

// Add read conditions
// Memory read/ Quantity format Number of bytes Memory address
// Short x/ quantity format Bytes Memory address
// The format x is hexadecimal, f is floating point, and d is base 10
B: byte 1 byte, H: half word 2 bytes, W: word 4 bytes, g: giant word 8 bytesExample: / x4xw    W means 4 bytes, x means read in hexadecimal format, and 4 means read 4 times
Copy the code

LLDB can also be used to change the value in memory

memory write 0x100400c68 6Change the value of _no to6
Copy the code

So how much memory does an NSObject take up? NSObjcet is actually a structure with only one pointer named ISA, and therefore takes up the amount of memory used by a pointer variable, 8 bytes if 64-bit or 4 bytes if 32bit.


More complicated inheritance

What is the output of the following code in a 64-bit environment?

/* Person */
@interface Person: NSObject {
    int _age;
}
@end
@implementation Person
@end
/* Student */
@interface Student: Person {
    int _no;
}
@end
@implementation Student
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"%zd %zd",
        class_getInstanceSize([Person class]),
        class_getInstanceSize([Student class])); }return 0;
}
Copy the code

According to the above analysis and discovery, the class object is essentially stored in memory in the form of structure, drawing a real memory legend

We found that whenever an object inherits from NSObject, there must be an ISA pointer in the underlying structure.

So how much memory do they take up? Simply add up the memory occupied by Pointers and member variables? The code above actually prints 16, 16, which means that both the Person and student objects take up 16 bytes of memory.

In fact, the Person object really only uses 12 bytes. But because of memory alignment. Make the Person object take 16 bytes as well.

We can summarize memory alignment as two principles:

  • Principle 1. The preceding address must be a positive multiple of the following address.
  • Principle 2. The address of the entire Struct must be an integer multiple of the largest byte.

The first address of the person object to hold the ISA pointer requires 8 bytes, and the second address to hold the _age member variable requires 4 bytes. According to principle 1, 8 is an integer multiple of 4, which meets principle 1 and does not need to be completed. Then check principle 2, the person object currently occupies 12 bytes of memory, not an integer multiple of the maximum byte size of 8 bytes, so 4 bytes need to be filled out, so the Person object occupies 16 bytes.

For student, we know that the student contains the struct implementation of the Person object and a _no member variable of type int. Again, the isa pointer is 8 bytes, the _age member variable is 4 bytes, and the _no member variable is 4 bytes, which satisfies principles 1 and 2. So the student object takes up 16 bytes of memory.


Where is the OC class information stored

OC objects can be divided into three main categories

  • Instance object (instance object)
  • Class object (Class object)
  • Meta-class objects (metaclass objects)

An instance object is an object that comes out of the alloc class, and every time you call that alloc, you create a new instance object

NSObjcet *object1 = [[NSObjcet alloc] init];
NSObjcet *object2 = [[NSObjcet alloc] init];
Copy the code

Object1 and Object2 are both Instace objects (instance objects) of NSObject, but they are two different objects and occupy two different pieces of memory. The instance object stores information in memory including

  • Isa pointer
  • Other member variables

Derivative question: Where is the code for the method of the instance object? So where does the method information of the class, the protocol information, the property information go?

Class object

We get a class object either through the class method or the Runtime method. A class object is a class object

Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
// runtime
Class objectClass4 = object_getClass(object1);
Class objectClass5 = object_getClass(object2);
NSLog(@"%p %p %p %p %p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);
Copy the code

Each class has one and only one class object in memory

You can prove by printing the memory address that the class object stores information in memory mainly including

  • 1. The isa pointer
  • 2. The superclass pointer
  • 3. Class attribute information (@Property), class member variable information (IVAR)
  • 4. Instance Method and Protocol of the class

The value of a member variable is stored in the instance object because the value of the member variable is assigned only when we create the instance object. But you only need to have a copy of the name and type of the member variable.

Where do class methods go? Metaclass object meta-class

// When you pass in a class object, you get a metaclass object
Class objectMetaClass = object_getClass([NSObject class]);
// When you call the class method of a class object, you get the same class object, no matter how many times you call it
Class cls = [[NSObject class] class];
Class objectClass3 = [NSObject class];
class_isMetaClass(objectMetaClass) // Determine whether the object is a metaclass
NSLog(@"%p %p %p", objectMetaClass, objectClass3, cls); // If you call class multiple times, you get the same class object
Copy the code

Each class has one and only one meta-class object in memory. Meta-class objects have the same memory structure as class objects, but their purpose is different. The information stored in the memory mainly includes

  • 1. The isa pointer
  • 2. The superclass pointer
  • 3. Class method information (class method)

Meta -class objects have the same memory structure as class objects, so meta-class objects also have member variables such as class attributes and object methods, but the values may be empty.

Where does the isa pointer to the object point?

1. When an object calls an instance method, as described above, the instance method information is stored in the class object, so to find the instance method, you must find the class object.

[stu studentMethod];
Copy the code

Instance isa refers to class. When an object method is called, the class is found through the instance ISA and the implementation of the object method is found.

2. When a class object calls a class method, as above, the class method is stored in a meta-class metaclass. To find the class method, you need to find the meta-class metaclass, and the ISA pointer to the class object points to the metaclass

[Student studentClassMethod];
Copy the code

When a class method is called, the class isa is used to find the meta-class, and the implementation of the class method is found to call

3. How to find the parent object method when the object calls its parent object method? , then you need to use the superclass pointer to the class object.

[stu personMethod];
[stu init];
Copy the code

When a Student instance calls a Method on a Person object, it finds the Student class through isa, the Person class through superclass, and the implementation of the object method. Similarly, if Person finds that it has no object method to respond to, it will find NSObject’s class object through Person’s superclass pointer to find the method to respond to

When a class object calls a class method of its parent class, it needs to find the meta-class through the ISA pointer, and then find the method in response through the superclass

[Student personClassMethod];
[Student load];
Copy the code

When a Student class calls a Person class method, Student’s meta-class is first identified by ISA, then Person’s meta-class is identified by superclass, and finally the implementation of the class method is identified

Finally, there is this stationary ISA pointing diagram. After the above analysis, when we look at this diagram, it becomes much clearer.

Summary of ISA and Superclass

  • 1. Instance isa points to class
  • 2. Class isa points to meta-class
  • 3. The META-class isa points to the meta-class of the base class, and the base class ISA points to itself
  • 4. The superclass of class points to the superclass of the superclass. If there is no superclass, the superclass pointer is nil
  • 5. The superclass of the meta-class refers to the superclass of the parent class, and the superclass of the base class refers to the class of the base class
  • Superclass = superclass; superclass = superclass; superclass = superclass
  • Superclass = superclass; superclass = superclass; superclass = superclass

How to prove that the ISA pointer really points as stated above?

We prove this by the following code:

NSObject *object = [[NSObject alloc] init];
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass([NSObject class]);
NSLog(@"%p %p %p", object, objectClass, objectMetaClass);
Copy the code

Interrupt point and print the ISA pointer to the corresponding object through the console

We find that object-> ISA is different from objectClass. This is because from 64bit, ISA needs to perform a bit operation to calculate the real address. The value of the bit operation can be found by downloading objC source code.

We verify this with a bit operation.

We find that object- ISA pointer address 0x001DffFF96537141 through the same 0x00007FFffffffffFFFFff8-bit operation, objectClass address 0x00007FFF96537140

We then verify that the ISA pointer to the class object also requires a bit operation to compute the address of the meta-class object. When we print the objectClass-> ISA pointer in the same way, we find that it cannot be printed

Also notice that there is no ISA pointer in the left objectClass object. Let’s take a look inside Class

typedef struct objc_class *Class;
struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
#if! __OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
Copy the code

The first object is an ISA pointer. In order to get the address of the ISA pointer, we create an identical structure and cast it to get the ISA pointer.

struct xx_cc_objc_class{
Class isa;
};
Class objectClass = [NSObject class];
struct xx_cc_objc_class *objectClass2 = (__bridge struct xx_cc_objc_class(*)objectClass);
Copy the code

Now let’s reverify that

Indeed, the bitwise address of objectClass2’s isa pointer isa meta-class address.


About the underlying implementation of OC Class

The nature of the Class

We know that both Class objects and metaclasses are of Class type. The underlying Class and mete-class are Pointers to the objc_class structure, which is the structure in memory. This chapter explores the nature of Class.

Class objectClass = [NSObject class];        
Class objectMetaClass = object_getClass([NSObject class]);
Copy the code

Click on Class to get inside and we can find out

typedef struct objc_class *Class;
Copy the code

The Class object is actually a pointer to the objc_class structure. So we can say that a class object or metaclass object is actually an objC_class structure in memory.

If we go inside objc_class, we can see this code that often appears in the underlying principles.

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
#if! __OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
Copy the code

This code is probably common in this article, but OBJC2_UNAVAILABLE; The code is no longer in use. So what is the current structure of objc_class? We look for the objC_class structure in objC source code

We find that this structure inherits objc_Object and has some functions in it, because this is a C ++ structure, extended on C, so the structure can contain functions. Let’s go inside objc_Object and intercept some of the code

We find that objc_Object has an ISA pointer, so objc_class inherits objc_Object and also has an ISA pointer

So where is the information that we saw before, the information that’s stored in a class about the member variables, instance methods, property names, etc. So we go to class_rw_T, and we intercept some code, and we find that class_rw_T stores a list of methods, a list of properties, a list of protocols, and so on.

Class_rw_t is obtained by using bits to call the data method, which is implemented inside the data method. As you can see, the data function internally only operates on bits &FAST_DATA_MASK

The member variable information is stored inside class_ro_T, so we go to class_RO_T

The final summary is summarized by a picture

We can customize a structure, and if the structure we write is the same as the real objC_class structure, then when we force the conversion, we will assign a one-to-one value. At this point we can get information from inside the structure.

The following code is a custom structure modeled after the objC_class structure by extracting the information needed to use it.

#import <Foundation/Foundation.h>
#ifndef XXClassInfo_h
#define XXClassInfo_h
# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif
#if __LP64__
    typedef uint32_t mask_t;
#else
    typedef uint16_t mask_t;
#endif
    typedef uintptr_t cache_key_t;
struct bucket_t {
    cache_key_t _key;
    IMP _imp;
};
struct cache_t {
    bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
};
struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
};
struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};
struct method_list_t : entsize_list_tt {
    method_t first;
};
struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
};
struct ivar_list_t : entsize_list_tt {
    ivar_t first;
};
struct property_t {
    const char *name;
    const char *attributes;
};
struct property_list_t : entsize_list_tt {
    property_t first;
};
struct chained_property_list {
    chained_property_list *next;
    uint32_t count;
    property_t list[0];
};
typedef uintptr_t protocol_ref_t;
    struct protocol_list_t {
    uintptr_t count;
    protocol_ref_t list[0];
};
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // The memory space occupied by the instance object
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;  / / the name of the class
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // List of member variables
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};
struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t * methods;    // List of methods
    property_list_t *properties;    // Attribute list
    const protocol_list_t * protocols;  // Protocol list
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};
#define FAST_DATA_MASK          0x00007ffffffffff8UL
struct class_data_bits_t {
    uintptr_t bits;
    public:
    class_rw_t* data() { // Provide the data() method for &fast_data_mask
        return (class_rw_t*)(bits & FAST_DATA_MASK); }};/* OC object */
struct xx_objc_object {
    void *isa;
};
/* Class object */
struct xx_objc_class : xx_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
    public:
    class_rw_t* data() {
        return bits.data();
    }
    xx_objc_class* metaClass(a) { // Provide the metaClass function to get the metaClass object
    // As we explained in the previous article, the isa pointer needs to pass the &isa_mask operation to get the real address
        return (xx_objc_class *)((long long)isa & ISA_MASK); }};#endif /* XXClassInfo_h */
Copy the code

Next we force our own class into our own simplified class structure type.

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "XXClassInfo.h"
/* Person */
@interface Person : NSObject <NSCopying> {
    @public
    int _age;
}
@property (nonatomic, assign) int height;
- (void)personMethod;
+ (void)personClassMethod;
@end
@implementation Person
- (void)personMethod {}
+ (void)personClassMethod {}
@end
/* Student */
@interface Student : Person <NSCoding> {
    @public
    int _no;
}
@property (nonatomic, assign) int score;
- (void)studentMethod;
+ (void)studentClassMethod;
@end
@implementation Student
- (void)studentMethod {}
+ (void)studentClassMethod {}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
    NSObject *object = [[NSObject alloc] init];
    Person *person = [[Person alloc] init];
    Student *student = [[Student alloc] init];
    xx_objc_class *objectClass = (__bridge xx_objc_class *)[object class];
    xx_objc_class *personClass = (__bridge xx_objc_class *)[person class];
    xx_objc_class *studentClass = (__bridge xx_objc_class *)[student class];
    xx_objc_class *objectMetaClass = objectClass->metaClass();
    xx_objc_class *personMetaClass = personClass->metaClass();
    xx_objc_class *studentMetaClass = studentClass->metaClass();
    class_rw_t *objectClassData = objectClass->data();
    class_rw_t *personClassData = personClass->data();
    class_rw_t *studentClassData = studentClass->data();
    class_rw_t *objectMetaClassData = objectMetaClass->data();
    class_rw_t *personMetaClassData = personMetaClass->data();
    class_rw_t *studentMetaClassData = studentMetaClass->data();
    // 0x00007ffffffffff8
    NSLog(@"%p %p %p %p %p %p",  objectClassData, personClassData, studentClassData,
    objectMetaClassData, personMetaClassData, studentMetaClassData);
    return 0;
}
Copy the code

We can see the information inside the class through the interrupt point.

At this point, we take out the classic graph again and analyze the isa pointer and superclass pointer in the graph one by one


The instance objects

First let’s look at the instance object. We learned from the previous article that the Instance object stores the ISA pointer and other member variables, and the ISA pointer of the instance object points to the address of its class object. ObjectClass, personClass, and studentClass are the three instance objects created in the above code.

From the above figure, we can see that the INSTANCE object does store the ISA pointer and its member variables, and the address calculated after the & operation of the ISA pointer of the instance object is indeed the memory address of its corresponding class object. Thus we prove that ISA, superclass, points to lines 1, 2 and 3 in the graph.


Class object

Then we look at the class object, also through the last article, we know that the class object stores isa pointer, superclass pointer, as well as the class attribute information, class member variables information, class object methods, and class protocol information. We know that this information is stored in the class_rw_T of the class object, and we spy on it by casting it. The following figure

In the figure above, we simulate calling the.data function on the Person object, which performs &fast_datA_mask (0x00007FFFFFFFF8ul) on bits and converts it to class_rw_t. PersonClassData in the figure above. We found that member variable information, object methods, attributes and other information only display the first. If you want to get more information, you need to move the pointer later through the code to obtain.

InstaceSize = 16 corresponds to the isa pointer to the Person object (8 bytes +_age4 bytes +_height4 bytes). ObjectClassData and studentClassData are not analyzed here. The basic content is the same as personClassData.

Do isa and superclass Pointers in class objects point like the classic illustration? So let’s verify that.

By analyzing the memory address in the figure above, we prove that ISA, superclass, points to lines 4, 5, and 6 of the ISA pointer in the figure, and to lines 10, 11, and 12 of the Superclass pointer.


Meta – class object

The meta-class contains the isa pointer, the superclass pointer, and the class method information of the class. We also know that meta-class metaclasses have the same structure as class metaclasses, but store different information, and that the ISA pointer of the metaclasses points to the base metaclasses, and the ISA pointer of the base metaclasses points to itself. A superclass pointer to a metaclass object points to a metaclass object of its parent class, and a superclass pointer to a metaclass object of its base class points to an object of its class.

As with the class object, we mock the person metaclass by calling the.data function, which does &fast_data_mask (0x00007FFFFFFFF8ul) to bits and converts it to class_rw_t.

First, we can see that the structure is the same as personClassData, and the member variables and attribute lists are empty, while methods stores the class method personClassMethod.

Next, verify that the ISA and Superclass Pointers point to the same number as the above.

The isa pointer to the base class points to itself. The isa pointer to the base class points to itself.

The superclass of the meta-class refers to the superclass of the superclass, and the superclass of the base class refers to the class of the base class.

Above post since: https://juejin.cn/post/6844903588150378509 and https://juejin.cn/post/6844903591228997640