It doesn’t matter how slowly you learn, as long as you’re not marking time, that’s progress!

preface

In the previous chapter, we explored the nature of objects and the process of isa relating to classes. We learned that an object is essentially a pointer to a structure and is associated to a class through its internal member variable isa. What isa class? So let’s move on to this chapter.

Class (Class)

In OC, everything is an object, and there is an ISA pointing to a class. In OC, there is an ISA pointing to a class, and there is an ISA pointing to a class.

isaPoint to the

  • objectisaPoint to the

Create an LhkhPerson class and instantiate an object p

If the object isa points to the class object ISA -> class, then the address of the class is 0x0000000100794520. Next LLDB debugging:

  • classisaPoint to the

LhkhPerson (LhkhPerson, LhkhPerson, LhkhPerson, LhkhPerson, LhkhPerson); So the isa of the class –> a class with the same name as the class.

  • classVSThe same class

So that makes us wonder why the pointer address is different, because it’s all LhkhPerson. Which is the actual pointer address of LhkhPerson? Let’s verify this with code:

The address of LhkhPerson is 0x00000001007944F8. The address of LhkhPerson is 0x00000001007944F8.

  • The metaclassThe birth of

The MachOView tool is used to analyze the machO executable

Then right click on theshow in Finder , right click to display the package contents, and drag the executable file toMachOViewOn the tools,

Through MachOView analysis, it can be seen that after the project is compiled, by looking at the symbol table, it is found that the system generates a metaclass for LhkhPerson, and the class corresponding to the problem left above is this metaclass.

  • The metaclass summary

    • From the above analysis,The metaclassIs generated by system compilation,classAnd its associatedThe metaclassIs the same;
    • The object’sisaPoint to theclass.classtheisaPoint to theThe metaclass.
  • The metaclassisaPoint to the

Does the metaclass have a class structure, or to whom does isa refer? Let’s move on to this question:

Continue through thelldbfoundThe metaclasstheisaPoint to theA metaclassAnd theA metaclasstheisaPoint to theA metaclassoneself

  • isaTo summarize

After debugging the above code and analyzing the MachOView tool, we finally get the point of ISA:

Object – isa – > class – isa – > metaclass – isa – > root metaclass – isa – > root metaclass

This gives you an ISA pointing graph

The pointing diagram of ISA has been explored. We usually have class inheritance in projects, so how does class inherit from 1?

classClass inheritance chain

– Class inheritance chain

LhkhTeacherIs inherited fromLhkhPerson.LhkhPersonIs inherited fromNSObject, by printingLhkhTeatherandLhkhPersonThere areNSObjectAnd the inheritance chain between them isLhkhTeatherInherited – > — –LhkhPerson— — — — — — inheritance>NSObjectSince classes have a chain of inheritance, what about metaclasses?

– Metaclass inheritance chain

LhkhPerson inherits from NSObject, so we print out its metaclass, the root metaclass, the root metaclass address is 0x7FFF80030638, and the LhkhPerson metaclass’s parent print address is also 0x7FFF80030638, and we get that address pointing to NSObject, The root metaclass; Is it possible to get any metaclass whose parent class is the root metaclass?

LhkhTeacherIs inherited fromLhkhPerson.LhkhPersonInherited fromNSObject, then if according to the above conclusion isLhkhTeacherThe parent class of the metaclass is also the root metaclass, but the print tells us that this is not the case. The parent class of the metaclass of the subclass is the metaclass of the parent class of the subclass. Why is this? Because metaclasses also have a chain of inheritance,LhkhTeacher metaclassInherited – > — –LhkhPerson metaclassInherited – > — –A metaclass NSObject. Now, the question I think you’re going to ask is, the chain of class inheritance goes to the root class NSObject, the metaclass inheritance goes to the root metaclass NSObject, so what do the root class NSObject and the root metaclass NSObject inherit from?

– Classes and metaclasses inherit special cases

According to the printed results, the parent class of the root class is null, that is, the root class has no parent class, and the parent class of the root metaclass is the root class.

– Summary of class inheritance chain

  1. A subclassinheritance—>The parent classinheritance—>The root class—>nil
  2. Subclasses metaclassinheritance—>The parent class metaclassinheritance—>A metaclassinheritance—>The root class—>nil

This gives you the inheritance diagram of the class

– Apple official ISA and class direction diagram

Class

Before exploring the underlying structure of classes we need to look at memory offsets

Memory offset supplement

To explore this, let’s use three examples: plain pointer, object pointer, and array pointer.

1. Common pointer

Two local variables are definedaandbThey’re all equal to 10, and you can see by printing that they all have a value of 10, and their addresses are different,aThe address for0x7ffee9501c24.bThe address for0x7ffee9501c20

The draw

  • According to theaandbIs a local variable, and its address0x7We know at the beginning that it’s stored inThe stack area;
  • The stack area address is from high to low, andaandbDifference between4Bytes, becauseaforintType, need to occupy4Bytes, soatobThe offset of is4Bytes.
2. Object pointer

We define two objects, obj1 and obj2, and we analyze them by printing the addresses:

  • It can be seen thatobj1The address for0x600002440100Is through theallocIt’s opened up, andobj2The address for0x600002440110throughallocOpen up, through0x6The beginning and they are manually created and we know that they are in the heap;
  • Let’s look at the take address of the two, which is the pointobj1andobj2The addresses of heap addresses, respectively, are0x7ffeec5bcc08and0x7ffeec5bcc00You can see that is in the stack area, and becauseobj1Bit object occupation8Bytes, soobj1toobj2The offset of is8Bytes.
3. Array pointer

Define an array pointerc, and a pointerd, the address of the array pointer C can be obtained by printing0x7ffeebfa1c20And thecThe address of the first element pointer to0x7ffeebfa1c20, the following element address is also every other4Bytes (because array Pointers contain ints, ints are 4 bytes); The pointerdThe initial assignment isc, so printdThe address is0x7ffeebfa1c20And d shifts by oneStep length(The step size is also determined by the type of the element in C, which is an int, so offset by 4 bytes); From your analysis above, we can get:

  • The first address of an array is the address of its first element, which is the memory addressThe first addressIt’s the address of the first element;
  • The address of the next element in the array can be obtained from the first address plus the offset of the previous element (depending on the type of element stored in the array)The first addressThe offsetMethod to get the address of the relative variable.

Class structure

In exploring the nature of the objectclassThe essence of aobjc_Class *Structure pointer, searched directly in objc source codeobjc_Class *Can be obtained;thenobjc_ClassWhat is it? Let’s search againobjc_ClassVolatilization can be defined in two ways

This one is no longer used in objC2, so just look at the one below

You can findobjc_ClassIs inherited fromobjc_object.By analyzing the source code, we can find that in addition to some methods, there are four internal member variables,ISA.superclass.cache.bits:

  • ISA: this is a hidden property inherited fromobjc_objectMember variables insideisa, is a pointer to a structure, which takes up 8 bytes and is mainly used to associate classes;
  • superclass: Also aClassType, which is essentially a pointer to the structure, which is the parent class of the current class, which also takes 8 bytes;
  • cache: is acache_tA type, essentially a structure, is used to optimize method calls, as discussed in a later section.
  • bits: is aclass_data_bits_tA type, essentially a structure, is used to store the attributes, methods, and other data of a class, which is the focus of our discussion.

So if we want to talk about bits, we have to get the address of the memory, based on the memory offset above, to get the address of the bits, we need to get the size of the cache, because cache_t is a structure so the size of the memory depends on the internal variable, So we don’t know the size yet, so let’s look at the internal structure of cache_t:

cacheMemory size

Through the source codecommandleft-clickcache_tThere are many methods and static variables in the cache_t structure. Since methods and global variables are not stored in the structure, they are not important, so we only need to look at the following two variables:

  • explicit_atomic<uintptr_t> _bucketsAndMaybeMask: is a generic structure whose true size is determined byuintptr_t(unsigned long integer, 8 bytes), so the occupancy can be obtained8Bytes;
  • A consortiumunion: contains only one variable, one structure,
    • There are three variables in this structure: we can conclude that the structure size is8byte
      • _maybeMaskA:mask_t(Uint32_t) type structure4bytes
      • _flags: uint16_tType take up2byte
      • _occupied: uint16_tType take up2byte
    • _originalPreoptCache: is a structure pointer type and occupies8byte

    If the union is mutually exclusive, then the size of the union is zero8Bytes.

union { struct { explicit_atomic<mask_t> _maybeMask; //4 #if __LP64__ uint16_t _flags; //2 #endif uint16_t _occupied; / / 2}; explicit_atomic<preopt_cache_t *> _originalPreoptCache; }Copy the code

So from the above analysis we can see that cache_t takes 8+8, which is 16 bytes, so we can calculate the addresses of bits, which is the first address +32 bytes offset (8+8+16).

bitsTo explore the

Find out

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

Class_rw_t: Class_rw_t: class_rw_t: class_rw_t: class_rw_t: class_rw_t: class_rw_t: class_rw_t

class_rw_tTo explore the

Using the source code, we went into class_rw_t. There are many methods in class_rw_t. We found several familiar ones:

This is where we store our methods, our properties, our protocols, and so onlldbTo explore:

  1. We’re throughx/4gx p.classGets the class whose first address is0x100008b20;
  2. Then through the above exploration out to want to obtainbitsThe address of is offset by 32 bytes, i.e0x20, and forcibly convert toclass_data_bits_t*, get the address is$2 = 0x0000000100008b40;
  3. throughreturn bits.data()We also passedp $2->data()get(class_rw_t *) $3 = 0x000000010126f810So far we’ve gotclass_rw_tThe pointer address of;
  4. Value by pointer address* $3The resultingclass_rw_tThe data in it.

So now that we haveclass_rw_tData structure, and we know it internally stored methods, properties, protocols, and so on, let’s see: this isLhkhPersonProperties and methods

  • attribute

  1. I got it from the topclass_rw_tWe take the value and get its property listp $4.properties();
  2. And you just take it layer by layerp $5.list.p $6.ptr, and then value by addressp *$7That’s when we actually get our list of properties;
  3. throughp $8.get(0)Get the first property, get ournameIt’s the same as what we defined.
  • Member variable supplement

We did not add member variables to the property sheet when we explored the properties above, so will the member variables also exist in the property sheet?

We found that we could only print oursnameProperty, if I try to get it again, it’s going to be an out-of-bounds error, so member variablesnickNameIt doesn’t exist in the property sheet, so where does it exist?

When we look at the implementation of the method class_rw_t, we find that there’s another method class_ro_t, and we go in and look at the implementation,

Class_ro_t = ivar_list_t *; class_ro_t = ivars; class_ro_t = ivar_list_t *

  1. We got that from the previous stepclass_rw_tAddress and value, and then throughp $4.ro()Access to theclass_ro_tAddress;
  2. We’re throughclass_ro_tAddress gets the value and then passes throughp $6.ivarsGets the address of the list of instance variables and passesp *$7The values;
  3. throughp $8.get(0)We got our instance variable in there, and we printed ournickNameMember variable;
  4. So let’s go ahead and evaluate and find out that it’s still in there_nameAnd we know thisnameIs the property that we defined, so we can also get what the system generates for the property_ the property nameDoes not existclass_ro_ttheivar_tIn the.

conclusion

The class_rw_t method is called within the class_rw_t method, and the ivars (ivar_list_t) instance variable ivar_t is used to store the member variables and the _ attribute names of the system-generated attributes.

  • methods

Let’s explore the method list in the same way:

Now, there’s one caveat here when we get the method name, becausemethod_tThe system does the processing, it’s a structure, and it puts data like the method name into a structure variable inside of itbigDown, so we need to put one after the method name here.big().

  • Class method supplement

So we go ahead and get the method, and after we find the getter and the setter of the property, we get out of bounds, but we don’t get the +(void)sayHello class method that we declared. What about the class method that we declared?

In fact, we know that the object method exists in the class, so we know that everything in OC is an object, then we guess that the class method will exist in the metaclass?

P /x object_getClass(lhkhperson.class); p/x object_getClass(lhkhPerson.class);

Object methods are stored in bits of the class structure, and class methods are stored in bits of the metaclass structure.

conclusion

Through the above exploration, we can know that the underlying structure of a class is mainly four variables ISA, superClass, cache and bits, and our bits store attributes, object methods, protocols and other data.