Directory:

  • 1: Superclass pointing analysis of ISA pointing and metaclasses
  • 1.1 Isa Mask (ISA_MASK
  • 1.2 ISA points to analysis
  • 1.3 Meta-class superclass pointing analysis
  • 2: Class structure analysis
  • 2.1 ObjC 1.0 objC_class (deprecated)
  • 2.2 ObjC 2.0 objC_class (based on objC4-818.2)
  • 2.2.1 ObjC-runtime-old. h ObjC_class definition in objc-runtime-old.h
  • 2.2.2 ObjC-Runtime-new. h Objc_class definition in objc-Runtime-new. h
  • 2.3 Analysis of objC_class member variable Bits
  • 2.3.1 Memory Translation
  • 2.3.2 Class structure memory calculation
  • 2.3.3 Obtaining class attributes
  • 2.3.4 Obtaining instance methods of a class
  • 2.3.5 Obtaining Member Variables of a Class (IVars)
  • 2.3.6 Obtaining the class method of a class
  • 2.3.7 Obtaining the protocol of the class

1:isaSuperclass pointing analysis of pointing and metaclasses

The book follows the nature of the object of the initial exploration of the underlying principle of OC (iii) Alloc exploration to continue to explore the principle of class.

1.1 isaMask (ISA_MASK)

Alloc exploration has already introduced ISA_MASK and its use method. Next, we need to use ISA_MASK. The current source version is divided into three architectures:

x86_64: define ISA_MASK                     0x00007ffffffffff8ULL // ULL: unsigned long long Unsigned long integer
arm64: define ISA_MASK                      0x0000000ffffffff8ULL
arm64(simulators): define ISA_MASK          0x007ffffffffffff8ULL
Copy the code

1.2 isaTo analyze

Code examples:

#import <Foundation/Foundation.h>
#import "XJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        XJPerson *p = [XJPerson alloc];
        NSLog(@ "% @",p);
    }
    return 0; } * * * * * * * * * * * * * * * * * * * * * * LLDB debugging result * * * * * * * * * * * * * * * * * * * * * * (LLDB)/x4gx p                                     // Format the memory information of object P in hexadecimal format
0x10044f160: 0x001d800100008365 0x0000000000000000 // get the isa of object p
0x10044f170: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100008365 & 0x00007ffffffffff8 // use isa & ISA_MASK as above
(long) $1 = 0x0000000100008360                     // Get the address of the class
(lldb) po 0x0000000100008360                       / / the Po output
XJPerson                                           / / get XJPerson

// Continue exploring
(lldb) x/4gx 0x0000000100008360                    // Output the memory information of the XJPerson class in hexadecimal format
0x100008360: 0x0000000100008338 0x00007fff932d8118 // Get the XJPerson class ISA
0x100008370: 0x00007fff6bbb6140 0x0000802c00000000
(lldb) p/x 0x0000000100008338 & 0x00007ffffffffff8 // isa & ISA_MASK
(long) $3 = 0x0000000100008338                     // Get a new address
(lldb) po 0x0000000100008338                       / / the Po output
XJPerson                                           // You can still get XJPerson

Copy the code

The XJPerson address is 0x0000000100008360 and 0x0000000100008338, respectively, and the XJPerson address is not the same. Let’s verify:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "XJPerson.h"

//MARK: - Analyze the memory number of class objects
void xjTestClassNum(void){
    Class class1 = [XJPerson class];
    Class class2 = [XJPerson alloc].class;
    Class class3 = object_getClass([XJPerson alloc]);
    Class class4 = [XJPerson alloc].class;
    NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        xjTestClassNum();
    }
    return 0; } * * * * * * * * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * * * * * * * *2021- 06- 19 15:39:55.257537+0800 002- the isa [5557:148166] 
0x100008360-
0x100008360-
0x100008360-
0x100008360
Program ended with exit code: 0

Copy the code

As you can see from the above example, no matter how many objects are generated, there is only one class. So why is the address Po output 0x0000000100008338 XJPerson? MachOView:

In addition to _OBJC_CLASS_$_XJPerson, a MachOView search class will also see _OBJC_METACLASS_$_XJPerson, which is the MetaClass. Our code does not actively generate metaclasses, indicating that the program actively generates them for us at runtime.

Now that you know the isa-> class object of an object and the ISA -> MetaClass of a class object, explore further:

// Omit some of the previous code and add the creation code for an instance object of the XJTeacher class, which inherits from XJPerson

// instantiate an object of class XJTeacher
XJTeacher *t = [XJTeacher alloc];
NSLog(@ "% @",t); * * * * * * * * * * * * * * * * * * * * * * LLDB debugging result * * * * * * * * * * * * * * * * * * * * * *// XJPerson LLDB debugging

// Omit some of the previous XJPerson class LLDB debugging

(lldb) po 0x0000000100008338                          // The metaclass of the XJPerson class
XJPerson

(lldb) x/4gx 0x0000000100008338                       // Format the memory information for the XJPerson metaclass in hexadecimal format
0x100008338: 0x00007fff932d80f0 0x00007fff932d80f0    // Get the ISA of the XJPerson metaclass
0x100008348: 0x0000000100406c50 0x0003e03500000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $5 = 0x00007fff932d80f0                        // Get the new address
(lldb) po 0x00007fff932d80f0                          / / the Po output
NSObject                                              // The output is NSObject, so is this a class or a metaclass

// XJTeacher LLDB debugging

(lldb) x/4gx t                                        // Format the memory information of object T in hexadecimal format
0x100522330: 0x001d800100008315 0x0000000000000000    // Get the isa of object t
0x100522340: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x001d800100008315 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $1 = 0x0000000100008310                        // Get the new address
(lldb) po 0x0000000100008310                          / / the Po output
XJTeacher                                             // The output is XJTeacher, i.e. class XJTeacher

(lldb) x/4gx 0x0000000100008310                       // Format the memory information of the XJTeacher class in hexadecimal
0x100008310: 0x00000001000082e8 0x0000000100008360    // Get class XJTeacher ISA
0x100008320: 0x00007fff6bbb6140 0x0000802c00000000
(lldb) p/x 0x00000001000082e8 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $3 = 0x00000001000082e8                        // Get the new address
(lldb) po 0x00000001000082e8                          / / the Po output
XJTeacher                                             // The output is XJTeacher, the metaclass XJTeacher

(lldb) x/4gx 0x00000001000082e8                       // Format the memory information of the metaclass XJTeacher in hexadecimal
0x1000082e8: 0x00007fff932d80f0 0x0000000100008338    // get the metaclass XJTeacher isa
0x1000082f8: 0x00000001006433a0 0x0003e03500000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $5 = 0x00007fff932d80f0                        
(lldb) po 0x00007fff932d80f0                          
NSObject                                              // The output is NSObject, so is this a class or a metaclass

NSObject LLDB debugging
// Let's verify

(lldb) p/x [NSObject class]                           // Outputs the memory information of NSObject's class in hexadecimal
(Class) $7 = 0x00007fff932d8118 NSObject              // Get the address of the root NSObject
(lldb) x/4gx 0x00007fff932d8118                       // Format the memory information for the root NSObject class in hexadecimal
0x7fff932d8118: 0x00007fff932d80f0 0x0000000000000000 // Get the root class NSObject isa
0x7fff932d8128: 0x00000001004992d0 0x0001801000000003
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8    // isa & ISA_MASK
(long) $8 = 0x00007fff932d80f0                        // Get the new address
(lldb) po 0x00007fff932d80f0                          / / the Po output
NSObject                                              // Output NSObject, the metaclass of the root class, RootMetaClass

// Continue validation
(lldb) x/4gx 0x00007fff932d80f0
0x7fff932d80f0: 0x00007fff932d80f0 0x00007fff932d8118
0x7fff932d8100: 0x0000000100643750 0x0005e03100000007
(lldb) p/x 0x00007fff932d80f0 & 0x00007ffffffffff8
(long) $18 = 0x00007fff932d80f0
(lldb) po 0x00007fff932d80f0
NSObject
Copy the code

Illustration:

Conclusion:

  1. The root classNSObjectClassisaPointing to the class (Class), the classisaPointing to metaclass (MetaClass), metaclassisaPoint to the root metaclass (RootMetaClass) instead of the parent metaclass.
  2. The root classNSObjectClassisaPoint to the root class (RootClass) of the root classisaPoint to the root metaclass (RootMetaClass).
  3. A metaclassisaAgain, it points to the root metaclass.

1.3 yuan classsuperclassTo analyze

Example code:

#pragmaMark - Superclass chain of metaclasses
void xjSuperclass(void) {
    
    //NSObjcet instance object
    NSObject *objectInstance = [NSObject alloc];
    / / NSObject class
    Class object = object_getClass(objectInstance);
    / / NSobject metaclass
    Class metaClass = object_getClass(object);
    / / NSObjct metaclass
    Class rootMetaClass = object_getClass(metaClass);
    
    NSLog(@"\n%p NSObject instance object \n%p NSObject class \n%p NSObject meta-class \n%p NSObject root metaclass \n",objectInstance,object,metaClass,rootMetaClass);
    
    // The metaclass for XJPerson
    Class personMetaClass = object_getClass(XJPerson.class);
    NSLog(@"XJPerson metaclass: %@ - %p",personMetaClass, personMetaClass);
    
    // The parent of the XJPerson metaclass
    Class personSuperMetaclass = class_getSuperclass(personMetaClass);
    NSLog(@"XJPerson metaclass parent: %@ - %p",personSuperMetaclass, personSuperMetaclass);
    
    // XJTeacher's metaclass
    Class teacherMetaClass = object_getClass(XJTeacher.class);
    NSLog(@"XJTeacher metaclass: %@ - %p",teacherMetaClass,teacherMetaClass);
    
    // the parent of the XJTeacher metaclass
    Class childrenSuperMetaClass = class_getSuperclass(teacherMetaClass);
    NSLog(@"XJTeacher metaclass parent: %@ - %p",childrenSuperMetaClass,childrenSuperMetaClass);
    
    // The parent of the NSObject class
    Class superObject = class_getSuperclass(NSObject.class);
    NSLog(@"NSObject parent: %@ - %p",superObject,superObject);
    
    //NSObject The parent of the root metaclass
    Class rootSuperMetaObject = class_getSuperclass(rootMetaClass);
    NSLog(@"NSObject Parent of root metaclass: %@ - %p",rootSuperMetaObject,rootSuperMetaObject); } * * * * * * * * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * * * * * * * *2021- 06- 19 23:10:35.443934+0800 002- the isa [12886:361372] 
0x100644850 NSObjectInstance objects0x7fff932d8118 NSObjectclass0x7fff932d80f0 NSObjectThe metaclass0x7fff932d80f0 NSObjectA metaclass2021- 06- 19 23:10:35.444578+0800 002- the isa [12886:361372] XJPerson metaclass: XJPerson -0x100008338
2021- 06- 19 23:10:35.444650+0800 002- the isa [12886:361372Parent of the XJPerson metaclass:NSObject - 0x7fff932d80f0
2021- 06- 19 23:10:35.444800+0800 002- the isa [12886:361372] XJTeacher metaclass: XJTeacher -0x1000082e8
2021- 06- 19 23:10:35.444838+0800 002- the isa [12886:361372] XJTeacher metaclass parent: XJPerson -0x100008338
2021- 06- 19 23:10:35.444908+0800 002- the isa [12886:361372] NSObjectParent class: (null) -0x0
2021- 06- 19 23:10:35.444942+0800 002- the isa [12886:361372] NSObjectThe parent of the root metaclass:NSObject - 0x7fff932d8118

Copy the code

Conclusion:

  1. There are inheritance chains between metaclasses, just like classes.
  2. The parent of the root metaclass is the root class.
  3. The parent of the root class is nil.

Illustration:

2: Class structure analysis

Struct objc_class = objC4-818.2 struct objc_class = objC4-818.2

ObjC 1.0 2.1objc_class(Deprecated)

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;
Copy the code

ObjC 2.0 2.2objc_class(based onObjc4-818.2 -)

2.2.1 objc runtime - old. HIn the fileobjc_classThe definition of

struct objc_class : objc_object {
    Class superclass;
    const char *name;
    uint32_t version;
    uint32_t info;
    uint32_t instance_size;
    struct old_ivar_list *ivars;
    struct old_method_list **methodLists;
    Cache cache;
    struct old_protocol_list *protocols;
    // CLS_EXT only
    const uint8_t *ivar_layout;
    struct old_class_ext *ext;
    
    // For reasons of length, most code is omitted
}
Copy the code

2.2.2 objc runtime - new. HIn the fileobjc_classThe definition of

// This definition is used in the current version and our exploration is based on this definition

struct objc_class : objc_object {
  objc_class(const objc_class&) = delete;
  objc_class(objc_class&&) = delete;
  void operator=(const objc_class&) = delete;
  void operator=(objc_class&&) = delete;
    // Class ISA; // 8 bytes, hidden variable isa, inherited from objc_object
    Class superclass;          / / 8 bytes
    cache_t cache;             // 16bytes, formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    
    // For reasons of length, most code is omitted
}
Copy the code

Illustration:

Objc-runtime-new. h contains four member variables: ISA (a hidden variable inherited from objc_Object), superclass, cache, and bits. (struct class_rw_t) (struct class_rw_t) (struct class_rw_t) (struct class_rw_t)

2.3 objc_classMember variablesbitsThe analysis of the

Case source:

@interface XJPerson : NSObject
{
    NSString *_age;
    CGFloat   _height;
}

@property (nonatomic.copy) NSString *name;

@property (nonatomic.copy) NSString *hobby;

- (void)sayNB;

+ (void)say666;

+ (void)heiha;

@end
Copy the code

Given the name of the objc_class, to obtain the bits member variable of the objc_class, you need to use the memory translation method.

2.3.1 Memory Translation

Example:

Illustration:

Conclusion: After the first address of a data structure is known, the information of its elements can be obtained by means of memory translation.

2.3.2 Class structure memory calculation

You want to get the bits in the data from the first address translation to the corresponding position of bits, the isa is 8 bytes, known superclass is 8 bytes, now need to calculate the memory size of the cache, the cache data structures for cache_t structure, view its definition (methods exist area, Cache_t: cache_t: cache_t: cache_t: cache_t: cache_t: cache_t: cache_t: cache_t: cache_t: cache_t: cache_t

// Uintptr_t rename
typedef unsigned long           uintptr_t;                      / / 8 bytes

// Mask_t rename, currently 64 bit environment, so 4 bytes
#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif

struct cache_t { // The cache_t structure has been calculated to occupy 16 bytes of memory
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;            / / 8 bytes
    union { _originalPreoptCache and _originalPreoptCache are mutually exclusive; they are both 8 bytes
        struct {
            explicit_atomic<mask_t>    _maybeMask;              // uint32_t 4 bytes
#if __LP64__
            uint16_t                   _flags;                  / / 2 bytes
#endif
            uint16_t                   _occupied;               / / 2 bytes
        };                                                      // Struct total 8 bytes
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; / / 8 bytes
    };
    
    // For reasons of length, most code is omitted
    
    // The following are static variables and methods that do not take up space in the structure body
}
Copy the code

Illustration:

2.3.3 Obtaining class attributes

Property acquisition process: xjperson. class -> class_data_bits_t -> class_rw_T -> property_array_t -> property_list_t -> property_t.

LLDB debugging process:

(lldb) p/x XJPerson.class
// Get the address of the XJPerson object
(Class) $0 = 0x0000000100008380 XJPerson           
(lldb) x/4gx 0x0000000100008380
// Get the first address of the XJPerson class object
0x100008380: 0x00000001000083a8 0x0000000100357140 
0x100008390: 0x000000010104e040 0x0002802800000003
// Shift the first address by 32 bits
(lldb) p/x 0x100008380 + 0x20    
// Get the class_datA_bits_t bits address
(long) $1 = 0x00000001000083a0
// Type change
(lldb) p (class_data_bits_t *)0x00000001000083a0   
(class_data_bits_t *) $2 = 0x00000001000083a0
// Pointers access member variables, methods use '->', and structs use '. '.
// Use the data() function in the class_data_bits_t structure to retrieve class_rw_t *
(lldb) p $2->data()                               
(class_rw_t *) $3 = 0x000000010104e000
// Use the properties() function inside the class_rw_t structure to retrieve property_array_t
(lldb) p $3->properties()                          
(const property_array_t) $4 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008260
      }
      arrayAndFlag = 4295000672}}}/ / remove the list
(lldb) p $4.list                                        
(const RawPtr<property_list_t>) $5 = {
  ptr = 0x0000000100008260
}
// get property_list_t pointer
(lldb) p $5.ptr
(property_list_t *const) $6 = 0x0000000100008260
// The * is a value
(lldb) p *$6
// 得到property_list_t结构体实例,count为2
(property_list_t) $7 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)}// Read the property value
(lldb) p $7.get(0)
(property_t) $8 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $7.get(1)
(property_t) $9 = (name = "hobby", attributes = "T@\"NSString\",C,N,V_hobby")
(lldb) 
Copy the code

Illustration:

2.3.4 Obtaining instance methods of a class

Instance methods, like properties, need to get class_rw_t, then call methods() to get the list of methods, and finally get method_t. But method_t has no member variables in the structure, so the direct output is empty. You need to get the big structure in method_t.

Instance method acquisition process: Xjperson. class -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> method_t->big

Method_t

Objc4-818.2 -> objc-runtimenew.h ->726-861

struct method_t {
    static const uint32_t smallMethodListFlag = 0x80000000;

    method_t(const method_t &other) = delete;

    // The representation of a "big" method. This is the traditional
    // representation of three pointers storing the selector, types
    // and implementation.
    struct big {
        SEL name;
        const char *types;
        MethodListIMP imp;
    };
    
    // For reasons of length, most code is omitted
}
Copy the code

LLDB debugging process:

(lldb) p/x XJPerson.class
// Get the address of the XJPerson object
(Class) $0 = 0x0000000100008380 XJPerson
(lldb) x/4gx 0x0000000100008380
// Get the first address of the XJPerson class object
0x100008380: 0x00000001000083a8 0x0000000100357140
0x100008390: 0x0000000100682630 0x0002802800000003
// Shift the first address by 32 bits
(lldb) p/x 0x100008380 + 0x20
// Get the class_datA_bits_t bits address
(long) $1 = 0x00000001000083a0
// Type change
(lldb) p/x (class_data_bits_t *)0x00000001000083a0
(class_data_bits_t *) $2 = 0x00000001000083a0
// Use the data() function in the class_data_bits_t structure to retrieve class_rw_t *
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001006825f0
// Retrieve method_array_t using the methods() function in the class_rw_t structure
(lldb) p $3->methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008160
      }
      arrayAndFlag = 4295000416}}}/ / remove the list
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
  ptr = 0x0000000100008160
}
// retrieve PTR, get method_list_t pointer
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x0000000100008160
// The * is a value
(lldb) p *$6
// Get an instance of method_list_t with count 6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)}// Get method_t with the get() function and read out the instance method information with the big() function
(lldb) p $7.get(0).big()
(method_t::big) $8 = {
  name = "sayNB"
  types = 0x0000000100003f7a "v16@0:8"
  imp = 0x0000000100003d40 (KCObjcBuild`-[XJPerson sayNB])
}
(lldb) p $7.get(1).big()
(method_t::big) $9 = {
  name = "hobby"
  types = 0x0000000100003f72 "@ @ 0:8 16"
  imp = 0x0000000100003db0 (KCObjcBuild`-[XJPerson hobby])
}
(lldb) p $7.get(2).big()
(method_t::big) $10 = {
  name = "setHobby:"
  types = 0x0000000100003f8e "v24@0:8@16"
  imp = 0x0000000100003de0 (KCObjcBuild`-[XJPerson setHobby:])
}
(lldb) p $7.get(3).big()
(method_t::big) $11 = {
  name = "init"
  types = 0x0000000100003f72 "@ @ 0:8 16"
  imp = 0x0000000100003ce0 (KCObjcBuild`-[XJPerson init])
}
(lldb) p $7.get(4).big()
(method_t::big) $12 = {
  name = "name"
  types = 0x0000000100003f72 "@ @ 0:8 16"
  imp = 0x0000000100003d50 (KCObjcBuild`-[XJPerson name])
}
(lldb) p $7.get(5).big()
(method_t::big) $13 = {
  name = "setName:"
  types = 0x0000000100003f8e "v24@0:8@16"
  imp = 0x0000000100003d80 (KCObjcBuild`-[XJPerson setName:])
}
(lldb) 
Copy the code

Illustration:

2.3.5 Obtaining Member Variables of a Classivars)

Member variables (IVars) : xjperson. class -> class_datA_bits_t -> class_rw_T -> class_ro_t -> ivar_list_t -> ivar_t.

Member variables (ivars), unlike attributes, reside in the class_ro_t structure.

Class_ro_t source code:

Objc4-818.2 -> objc-runtimenew.h ->1037-1171
struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars; // Member variables

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    
    
    // For reasons of length, most code is omitted
}

Objc4-818.2 -> objC-runtimenew.h ->863-883
struct ivar_t {
#if __x86_64__
    // *offset was originally 64-bit on some x86_64 platforms.
    // We read and write only 32 bits of it.
    // Some metadata provides all 64 bits. This is harmless for unsigned 
    // little-endian values.
    // Some code uses all 64 bits. class_addIvar() over-allocates the 
    // offset for their benefit.
#endif
    int32_t *offset;
    const char *name;
    const char *type;
    // alignment is sometimes -1; use alignment() instead
    uint32_t alignment_raw;
    uint32_t size;

    uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1<< alignment_raw; }};Copy the code

LLDB debugging process:

// Omit the process for getting class_rw_t
(class_rw_t *) $3 = 0x00000001007cfe30
// Call ro() of class_rw_t to get class_ro_t
(lldb) p $3->ro()
(const class_ro_t *) $4 = 0x0000000100008110
/ / value
(lldb) p *$4
// Get the class_ro_t structure
(const class_ro_t) $5 = {
  flags = 0
  instanceStart = 8
  instanceSize = 40
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char* > ="XJPerson" {
      Value = 0x0000000100003e96 "XJPerson"
    }
  }
  baseMethodList = 0x0000000100003de0
  baseProtocols = nil
  ivars = 0x0000000100008158 // Member variables
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000081e0
  _swiftMetadataInitializer_NEVER_USE = {}
}
// the. Syntax retrieves the list of member variables
(lldb) p $5.ivars
// Get the ivar_list_t pointer
(const ivar_list_t *const) $6 = 0x0000000100008158
/ / value
(lldb) p *$6
// get ivar_list_t, count = 4
(const ivar_list_t) $7 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)}// Get ivar_t via get()
(lldb) p $7.get(0)
(ivar_t) $8 = {
  offset = 0x00000001000082bc
  name = 0x0000000100003f10 "_age"
  type = 0x0000000100003f55 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
  offset = 0x00000001000082c0
  name = 0x0000000100003f15 "_height"
  type = 0x0000000100003f7c "d"
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
  offset = 0x00000001000082c4
  name = 0x0000000100003f1d "_name"
  type = 0x0000000100003f55 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $7.get(3)
(ivar_t) $11 = {
  offset = 0x00000001000082c8
  name = 0x0000000100003f23 "_hobby"
  type = 0x0000000100003f55 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
Copy the code

Illustration:

2.3.6 Obtaining the class method of a class

Class methods exist in metaclass, so you need to get the metaclass first, and then get the list of metaclass methods, to get the class method.

Class method acquisition process: XJPerson.class -> Metaclass -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t -> Method_t ->big

LLDB debugging process:

// M1 iMac debugging
(lldb) p/x XJPerson.class
(Class) $0 = 0x0000000100008328 XJPerson
(lldb) x/4gx 0x0000000100008328
0x100008328: 0x0000000100008350 0x0000000100379140
0x100008338: 0x000000010036d0f0 0x0000803000000000
(lldb) p/x 0x0000000100008350 & 0x0000000ffffffff8
(long) $1 = 0x0000000100008350
(lldb) x/4gx 0x0000000100008350
0x100008350: 0x00000001003790f0 0x00000001003790f0
0x100008360: 0x00010001009065f0 0x0002e03400000000
(lldb) p/x 0x100008350 + 0x20
(long) $2 = 0x0000000100008370
(lldb) p/x (class_data_bits_t *)0x0000000100008370
(class_data_bits_t *) $3 = 0x0000000100008370
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100a10e10
(lldb) p $4->methods()
(const method_array_t) $5 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003db8
      }
      arrayAndFlag = 4294983096
    }
  }
}
(lldb) p $5.list
(const method_list_t_authed_ptr<method_list_t>) $6 = {
  ptr = 0x0000000100003db8
}
(lldb) p $6.ptr
(method_list_t *const) $7 = 0x0000000100003db8
(lldb) p *$7
(method_list_t) $8 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 2)
}
(lldb) p $8.get(0).small()
(method_t::small) $9 = {
  name = (offset = 17608)
  types = (offset = 419)
  imp = (offset = - 272.)
}
(lldb) p $8.get(1).small()
(method_t::small) $10 = {
  name = (offset = 17604)
  types = (offset = 407)
  imp = (offset = - 264.)
}
(lldb) p $8.get(0).getDescription()
(objc_method_description *) $9 = 0x0000000100717a80
(lldb) p *$9
(objc_method_description) $10 = (name = "say666", types = "v16@0:8")
(lldb) p *($8.get(1).getDescription())
(objc_method_description) $11 = (name = "heiha", types = "v16@0:8")
Copy the code

Illustration:

Conclusion: A class is a unique object of a metaclass, and a class method is an instance method of a metaclass.

2.3.7 Obtaining the protocol of the class

To obtain the protocol of a class, you must first comply with the protocol.

Protocol obtaining process: XJPerson.class -> class_data_bits_t -> class_rw_t -> protocol_array_t -> protocol_list_t -> protocol_ref_t -> protocol_t -> list_t -> big

Protocol_list_t, PROTOCOL_ref_t, protocol_t

Sample code to test:

LLDB debugging process:

(lldb) p/x XJPerson.class
(Class) $0 = 0x0000000100008778 XJPerson
(lldb) p/x 0x0000000100008778 + 0x20
(long) $1 = 0x0000000100008798
(lldb) p (class_data_bits_t *)0x0000000100008798
(class_data_bits_t *) $2 = 0x0000000100008798
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001009c5d90
(lldb) p $3->protocols()
(const protocol_array_t) $4 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008168
      }
      arrayAndFlag = 4295000424
    }
  }
}
(lldb) p $4.list
(const RawPtr<protocol_list_t>) $5 = {
  ptr = 0x0000000100008168
}
(lldb) p $5.ptr
(protocol_list_t *const) $6 = 0x0000000100008168
(lldb) p *$6
(protocol_list_t) $7 = (count = 2, list = protocol_ref_t [] @ 0x00000001196abc58)
(lldb) p $7.list[0]
(protocol_ref_t) $8 = 4295002064
(lldb) p (protocol_t *)4295002064
(protocol_t *) $9 = 0x00000001000087d0
p $9[0]
(protocol_t) $10 = {
  objc_object = {
    isa = {
      bits = 4298608840
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537326105
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003e52 "XJPersonDelegate"
  protocols = 0x00000001000085b0
  instanceMethods = 0x00000001000085c8
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008600
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $10.instanceMethods
(method_list_t *) $11 = 0x00000001000085c8
(lldb) p $11->get(0).big()
(method_t::big) $12 = {
  name = "delegateMethod1"
  types = 0x0000000100003e77 "v16@0:8"
  imp = 0x0000000000000000
}
(lldb) p $11->get(1).big()
(method_t::big) $13 = {
  name = "delegateMethod2:"
  types = 0x0000000100003ea9 "@ 24 @ 0:8 @ 16"
  imp = 0x0000000000000000
}
(lldb) p $9[1]
(protocol_t) $14 = {
  objc_object = {
    isa = {
      bits = 4298608840
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537326105
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003e36 "XJPersonDataSource"
  protocols = 0x0000000100008278
  instanceMethods = 0x0000000100008290
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x00000001000082c8
  _demangledName = 0x0000000000000000
  _classProperties = nil
}
(lldb) p $14.instanceMethods
(method_list_t *) $15 = 0x0000000100008290
(lldb) p $15->get(0).big()
(method_t::big) $16 = {
  name = "dataSourceMethod1"
  types = 0x0000000100003e77 "v16@0:8"
  imp = 0x0000000000000000
}
(lldb) p $15->get(1).big()
(method_t::big) $17 = {
  name = "dataSourceMethod2:"
  types = 0x0000000100003ea9 "@ 24 @ 0:8 @ 16"
  imp = 0x0000000000000000
}
Copy the code

Illustration: