Isa to metaclass

Let’s define an ApplePerson class

@interface ApplePerson : NSObject
@end
Copy the code

Add a breakpoint

X p Print the object pointer address

(lldb) x p
0x100553e50: 69 84 00 00 01 00 00 01 00 00 00 00 00 00 00 00  i...............
0x100553e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00.Copy the code

The address is 0x100553E50, and the address is pointer below p/x

(lldb) p/x p
(ApplePerson *) $1 = 0x0000000100553e50
Copy the code

0x0000000100553E50 = 0x100553E50 = 0x100553e50 = x/4gx

(lldb) x/4gx p
0x100553e50: 0x0100000100008469 0x0000000000000000
0x100553e60: 0x0000000000000000 0x0000000000000000
Copy the code

0x0100000100008469 is the isa address, and the ARM64 ISA mask ISA_MASK & is the address of the class

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
Copy the code

ol

(lldb) p/x 0x0100000100008469 & 0x0000000ffffffff8
(long) $3 = 0x0000000100008468
Copy the code

Po the address of the $3 class variable

(lldb) po 0x0000000100008468
ApplePerson
Copy the code

So ApplePerson, what if I did x over 4gx for the class address

(lldb) x/4gx 0x0000000100008468
0x100008468: 0x0000000100008440 0x00000001eb780d08
0x100008478: 0x0000000180218e60 0x0000802900000000
Copy the code

Get an address structure similar to the above, if assuming 0x0000000100008440 is an ISA, apply it to the mask &

(lldb) po 0x0000000100008440 & 0x0000000ffffffff8
ApplePerson
Copy the code

It’s also an ApplePerson so I’m going to ampersand it again

(lldb) p/x 0x0000000100008440 & 0x0000000ffffffff8
(long) $7 = 0x0000000100008440
Copy the code

The address 0x0000000100008468 and 0x0000000100008440 Po are both ApplePerson. We suspect that more than one class has been created in memory

// Analyze the number of class objects in memory
void appleTestClassNum(void){
    Class cls1 = [ApplePerson class];
    Class cls2 = [ApplePerson alloc].class;
    Class cls3 = object_getClass([ApplePerson alloc]);
    Class cls4 = [ApplePerson alloc].class;
    NSLog(@"\n- %p \n- %p \n- %p \n- %p",cls1,cls2,cls3,cls4);
}
Copy the code

A printout

- 0x100008468 
- 0x100008468 
- 0x100008468 
- 0x100008468
Copy the code

0x100008468 is the same as 0x0000000100008468, which is the address of the class, and 0x0000000100008440 is not the address of the class, so it is not a class

Analysis: The object’s ISA points to the class, and the class’s ISA points to the new structure

Graph LR objects.- Object isa ====> class.- Class Isa ====> new structure

Next, we click execute file

Pull it into the rotten apple

Open the objC_classRefs column and open the ObjC2 References column, and you can see that there are only two classes ApplePerson and AppleEmployee

Click on the symbol table to query for class

You can see an _OBJC_CLASS_ApplePerson and an _OBJC_METACLASS_ApplePerson, and the METACLASS is the METACLASS, and we didn’t create the METACLASS, so it’s system-generated

Graph LR objects.- Object isa ====> class.- Class Isa ====> metaclass

Isa goes bitmap and inheritance chain

We found the metaclass above, what is in front of that metaclass, and then we run the project again, same code above, and print the breakpoint, okay

ApplePerson *p = [ApplePerson alloc];
NSLog(@"% @",p);
Copy the code

X /4gx to get the pointer address

(lldb) x/4gx p
0x100617ea0: 0x0100000100008299 0x0000000000000000
0x100617eb0: 0x0000000000000000 0x0000000000000000
Copy the code

And isa_mask & to get the class address

(lldb) p/x 0x0100000100008299 & 0x0000000ffffffff8
(long) $1 = 0x0000000100008298
Copy the code

Print the Po

(lldb) po 0x0000000100008298
ApplePerson
Copy the code

Perform x/4gx on the class address

(lldb) x/4gx 0x0000000100008298
0x100008298: 0x0000000100008270 0x00000001eb780d08
0x1000082a8: 0x0000000180218e60 0x0000802900000000
Copy the code

Continue with isa_mask & to get the metaclass address

(lldb) p/x 0x0000000100008270 & 0x0000000ffffffff8
(long) $3 = 0x0000000100008270
Copy the code

Please print it again

(lldb) po 0x0000000100008270
ApplePerson
Copy the code

If we go ahead and do x/4gx on the metaclass

(lldb) x/4gx 0x0000000100008270
0x100008270: 0x00000001eb780ce0 0x00000001eb780ce0
0x100008280: 0x00010001050376f0 0x0002e03500000000
Copy the code

Continue with isa_mask &

(lldb) p/x 0x00000001eb780ce0 & 0x0000000ffffffff8
(long) $5 = 0x00000001eb780ce0
(lldb) 
Copy the code

We get $5 and the address 0x00000001eb780CE0 is exactly the same as the metaclass address above so we print $5

(lldb) po 0x00000001eb780ce0
NSObject
(lldb) 
Copy the code

It’s NSObject, and you get the root metaclass

Graph LR object.- Object isa===> class.- Class ISA ===> metaclass.- metaclass ISA ===> root metaclass

To simplify the

Graph LR Isa of objects --> Isa of classes --> ISA of metaclass --> root metaclass

Now, let’s print the address of class NSObject again

(lldb) p/x NSObject.class
(Class) $7 = 0x00000001eb780d08 NSObject
(lldb) 
Copy the code

Obviously, $7 = 0x00000001eb780D08 is not consistent with the above 0x00000001eb780CE0, we will evaluate it again x/4gx

(lldb) x/4gx 0x00000001eb780d08
0x1eb780d08: 0x00000001eb780ce0 0x0000000000000000
0x1eb780d18: 0x0001000100612180 0x0001801000000000
Copy the code

Then get isa’s address and x/ 4Gx

(lldb) x/4gx 0x00000001eb780ce0
0x1eb780ce0: 0x00000001eb780ce0 0x00000001eb780d08
0x1eb780cf0: 0x0001000100704080 0x0002e03400000000
Copy the code

0x00000001eb780CE0 is the same as above, and isa_mask &

(lldb) p/x 0x00000001eb780ce0 & 0x0000000ffffffff8
(long) $8 = 0x00000001eb780ce0
Copy the code

Again, 0x00000001EB780CE0

Graph LR root class.- Root class isa ===> root metaclass

Therefore, we speculate the following class structure diagram:

Graph LR root class object.. - > root class... -> Root metaclass parent object.. - > the parent class.. -> Parent metaclass parent metaclass.. -> Root metaclass subclass object.. - > subclass.. -> metaclass of subclass metaclass of subclass.. -> root metaclass Root metaclass

Next, we verify the relationships between metaclasses through code

void appleTestNSObject(void){
// Initialize the NSObject root class instance object
    NSObject *obj = [NSObject alloc];
// Get the NSObject root class
    Class obj_class = object_getClass(obj);
// Get the root metaclass of NSObject
    Class meta_obj_class = object_getClass(obj_class);
// Get the root metaclass of NSObject
    Class root_meta_obj_class = object_getClass(meta_obj_class);
// Get the root root metaclass of NSObject
    Class root_root_meta_obj_class = object_getClass(root_meta_obj_class);
    
    NSLog(@"\n %p root class instance object \n %p root class \n %p root metaclass \n %p root metaclass \n %p root metaclass \n",obj,obj_class,meta_obj_class,root_meta_obj_class,root_root_meta_obj_class);
}
Copy the code

A print

 0x10512b040The root class instance object0x1eb780d08The root class0x1eb780ce0A metaclass0x1eb780ce0Spikes metaclass0x1eb780ce0Root root root metaclassCopy the code

You can see that NSObject’s root metaclass and all the way up is pointing to the same address, pointing to the root metaclass itself and let’s test the metaclass pointing to AppleEmployee

 // the AppleEmployee metaclass
    Class AppleEmployee_MetaClass = object_getClass(AppleEmployee.class);
    // The metaclass of the AppleEmployee parent
    Class AppleEmployee_SuperMetaClass = class_getSuperclass(AppleEmployee_MetaClass);
    / / AppleEmployee metaclass
    Class AppleEmployee_RootMetaClass = class_getSuperclass(AppleEmployee_SuperMetaClass);
    
    NSLog(@"Subclass metaclass: %@ - subclass metaclass address %p",AppleEmployee_MetaClass,AppleEmployee_MetaClass);
    NSLog(@"Parent metaclass: %@ - parent metaclass address %p",AppleEmployee_SuperMetaClass,AppleEmployee_SuperMetaClass);
    NSLog(@"Subclass root metaclass: %@ - subclass root metaclass address %p",AppleEmployee_RootMetaClass,AppleEmployee_RootMetaClass);
Copy the code

Print the following results:

Subclass metaclass: AppleEmployee - Address of the subclass metaclass0x1000082c0Parent metaclass: ApplePerson - Address of the parent metaclass0x100008270Subclass root metaclass: NSObject - Address of the subclass root metaclass0x1eb780ce0
Copy the code

We can see that the metaclass of the subclass points to the metaclass of the parent class, and the metaclass of the parent class points to the root metaclass, so we get the inheritance chain of the metaclass:

Graph BT subclass metaclass --> parent metaclass --> root metaclass

And the inheritance chain of subclasses

    / / AppleEmployee parent class
    Class AppleEmployee_SuperClass = class_getSuperclass(AppleEmployee.class);
    / / AppleEmployee root class
    Class AppleEmployee_rootClass = class_getSuperclass(AppleEmployee_SuperClass);
    // The AppleEmployee root class
    Class AppleEmployee_rootrootClass = class_getSuperclass(AppleEmployee_rootClass);
    NSLog(@"\nAppleEmployee parent class - %@\nAppleEmployee root class - %@\nAppleEmployee root class - %@",AppleEmployee_SuperClass,AppleEmployee_rootClass,AppleEmployee_rootrootClass);
Copy the code

A print

AppleEmployee parent class - ApplePerson AppleEmployee root class - NSObject AppleEmployee root class - (null)
Copy the code

So, we get the inheritance chain of the subclasses

Graph BT subclass --> Parent --> root --> nil

Let’s look for the parent of the root metaclass

// The parent of the root metaclass
    Class rootMetaClass__superClass = class_getSuperclass(root_meta_obj_class);
    NSLog(@"%@ - %p", rootMetaClass__superClass,rootMetaClass__superClass);
Copy the code

print

NSObject - 0x1eb780d08
Copy the code

As you can see, the parent of the root metaclass is the root class

Graph BT root metaclass --> root class

Finally, we have the isa and continuation class structure diagram.

Graph LR root metaclass object.-> root metaclass object.-> parent -> parent -> parent -> root metaclass object.-> Parent -> parent -> root metaclass object --> root --> nil metaclass --> parent metaclass --> root metaclass --> root class

It’s the same as the picture on apple’s website

Third, the structure of the source analysis class

We know that the object of a class has isa, has member variables, what’s the structure of that class

From the source code, we know that the class is a structure pointer of type objC_class

Looking at the underlying source code of objc_class, you can see that objc_class is the main thing that inherits from objc_Object

    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
Copy the code

Superclass is a superclass. Cache and bits are class_rw_t

class_rw_t *data() const {
        return bits.data();
    }
Copy the code

Class_rw_t returns bits.data. Click class_rw_t to see a list of methods and attributes

struct class_rw_t {
    ...
    const method_array_t methods() const{... }const property_array_t properties() const{... }}Copy the code

Pointer and memory translation

Now we know that a class needs to get its bits, get the data in the class, and the data in the class structure is also contiguous, much like an array. We know that an array is a structure that accesses the same data type, and the data in the memory segment is contiguous, and each data is the same size. We’re trying to get the data from an array in memory and we’re going to define an array and we’re going to point to that array

int intArr[4] = {1.2.3.4};
int *p = intArr;
TYLog(@"%p",&intArr);
TYLog(@"%p - %p - %p",&intArr[0],&intArr[1],&intArr[2]);
TYLog(@"%p - %p - %p",p+0,p+1,p+2);
Copy the code

For printing

TY to print:0x16fdff350TY to print:0x16fdff350 - 0x16fdff354 - 0x16fdff358TY to print:0x16fdff350 - 0x16fdff354 - 0x16fdff358
Copy the code

You can see the address of the array the address of the first element is also the address of the first element of the array. Shifting the array pointer by 1 int points to the next element of the array

for (int i = 0; i < 4; i++) {
    int value = *(p + i);
    TYLog(@"%d",value);  
}
Copy the code

Print the result

TY to print:1TY to print:2TY to print:3TY to print:4
Copy the code

Fifth, class structure memory calculation

We know that the address of the class is the first address of the memory structure of the class. Following the above idea, can we shift a certain size to get something else in the class structure and run down the code

Print the memory structure of the class

(lldb) x/4gx ApplePerson.class
0x100008468: 0x0000000100008440 0x00000001f7d48d08
0x100008478: 0x000000018c7e0e60 0x0000802900000000
(lldb) 
Copy the code

Look back at the structure of the class

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags... }Copy the code

The first address of the class is ISA, which we have already verified, and the second is superclass, which we will test

(lldb) po 0x00000001f7d48d08
NSObject
Copy the code

The exact parent of NSObject

@interface ApplePerson : NSObject
Copy the code

To get the address of class_data_bits_t we need the first address of the class + the size of the isa + the size of the superclass + the size of the cache. Isa is 8 bytes, superclass isa pointer to a structure, also 8 bytes. Cache_t isa structure. Instead of looking at static variables and methods, the member variables are the following two

Struct cache_t {... explicit_atomic<uintptr_t> _maskAndBuckets; mask_t _mask_unused; ... }Copy the code

Uintptr_t is 8 bits, mask_t check it out

#if __LP64__
typedef uint32_t mask_t;
Copy the code

Uint32_t = 8 bytes aligned according to the memory structure of the class, so cache_t is 16 bytes, so we can get bits by translating the address of the class 8 + 8 + 16 = 32 = 0x20

LLDB analyzes the structure of the class and the bits data of the class

Print the memory structure of the class again

(lldb) x/6gx ApplePerson.class
0x1000083d0: 0x00000001000083a8 0x0000000100379140
0x1000083e0: 0x000000010036d0e0 0x0000802800000000
0x1000083f0: 0x0000000100992be4 0x0000000100008420
Copy the code

0x1000083D0 Offset 32 bits

(lldb) p/x 0x1000083d0 + 0x20
(long) $1 = 0x00000001000083f0
Copy the code

This parameter is consistent with 0x1000083f0, because the bits to be verified are of class_DATA_bits_t type, the value is strongly converted

(lldb) p (class_data_bits_t *)0x00000001000083f0
(class_data_bits_t *) $2 = 0x00000001000083f0
Copy the code

At this point, bits will be retrieved by class_rw_t of the source code

class_rw_t *data() const {
    return bits.data();
}
Copy the code

Bits. Data returns *data().

(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100992be0
Copy the code

Class_rw_t *data() returns the same type as class_rw_t *data()

(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 0
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000200
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
Copy the code

Struct class_rw_t = struct class_rw_t = struct class_rw_t

const method_array_t methods() const{... }const property_array_t properties() const{... }Copy the code

There are two struct methods() and properties()

lldb) p $3.properties()
(const property_array_t) $5 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008138
      }
      arrayAndFlag = 4295000376
    }
  }
}
  Fix-it applied, fixed expression was: 
    $3->properties()
Copy the code

There’s a list in there

(lldb) p $5.list
(const RawPtr<property_list_t>) $6 = {
  ptr = 0x0000000100008138
}
Copy the code

A PTR appears

(lldb) p $6.ptr
(property_list_t *const) $7 = 0x0000000100008138
Copy the code

Direct access to

(lldb) p *$7
(property_list_t) $8 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)}Copy the code

The return type is property_list_t, so let’s see

struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {};Copy the code

Look at entsize_list_tt and see that get(number) returns data

Struct entsize_list_tt {... Element& get(uint32_t i)const { 
       ASSERT(i < count);
       returngetOrEnd(i); }... }Copy the code

To view the property_t

struct property_t {
    const char *name;
    const char *attributes;
};
Copy the code

Get (number)

(lldb) p $8.get(0)
(property_t) $9 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $8.get(1)
(property_t) $10 = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
p $8.get(2)
Assertion failed: (i < count), function get.file /Users/... /runtime/objc-runtime-new.h.line 624.
error: Execution was interrupted.reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
Copy the code

You can see that the class’s property parameters are consistent, but the member variable subject does not print the class’s methods

(lldb) p $3.methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100003d70
      }
      arrayAndFlag = 4294983024
    }
  }
}
  Fix-it applied, fixed expression was: 
    $3->methods()
Copy the code

Get the list

(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
  ptr = 0x0000000100003d70
}
Copy the code

To get the PTR

(lldb) p $5.ptr
(method_list_t *const) $6 = 0x0000000100003d70
Copy the code

Get method list

(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 4294901763.method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 5)}Copy the code

Count = 5, there are 5 methods, and methods are of type method_array_t

const method_array_t methods() const{... }Copy the code

Look at method_array_t, which is a list_array_TT structure

class method_array_t : 
    public list_array_tt<method_t.method_list_t.method_list_t_authed_ptr>
{... }Copy the code

print

(lldb) p $7.get(0).name()
(SEL) $8 = "sayHenXi"
(lldb) p $7.get(1).name()
(SEL) $9 = "name"
(lldb) p $7.get(2).name()
(SEL) $10 = "setName:"
(lldb)  p $7.get(3).name()
(SEL) $11 = "nickName"
(lldb)  p $7.get(4).name()
(SEL) $12 = "setNickName:"
(lldb) p $7.get(5).name()
Assertion failed: (i < count), function get.file /Users/... /runtime/objc-runtime-new.h.line 624.
Copy the code

Compare that to xcode

@interface ApplePerson : NSObject{
    NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *nickName;

- (void)sayHenXi;
+ (void)sayNiuBi;
@end
Copy the code

You can see that there are five methods, four of which are set/get methods for attributes and one of which is sayHenXi. The class sayNiuBi method is not listed in the bits.data method list