preface

The sun is shining, the weather is cool, don’t go out for a wave, in this kind of research, I degenerate ah. Anyway, the association between ISA and classes was discovered earlier while exploring objects. So let’s explore classes today, and see what’s so amazing about classes.

The preparatory work

Memory migration

In the previous blog, the nature of objects in IOS Underlying Principles &isa Association class explored the underlying implementation of the first address + offset value of the object if you want to get variables in the memory of the object. Let’s explore memory offsets

Ordinary pointer

int main(int argc, char * argv[]) {
    @autoreleasepool {
       int a = 10;
       int b = 10;
       int * p = &a;
       int * q = &b;
       NSLog(@"---%d----%p---%p",a,p,&p);
       NSLog(@"---%d----%p---%p",b,q,&q);
    }
    return 0;
}
Copy the code
2021-06-17 15:07:03.252239+0800 Memory offset [11954:347138] --10---- 0x7ffedFe69C8c -- 0x7FFedFe69C80 2021-06-17 15:07:03.252592+0800 Memory offset [11954:347138] --10---- 0x7ffedFe69c88 -- 0x7ffedFe69C78Copy the code
  • aandbThe values are10, butaandbThat’s what they sayDeep copy
  • aThe address is0x7ffedfe69c8c.bThe address is0x7ffedfe69c88, the difference between4Bytes, depending onaThe type of
  • a>b>p>qAnd all of them are inThe stack area.

The illustration below

Pointer to the object

int main(int argc, char * argv[]) {
    @autoreleasepool {
       LWPerson  * p1 = [LWPerson alloc];
       LWPerson  * p2 = [LWPerson alloc];
       NSLog(@"---%@----%p",p1,&p1);
       NSLog(@"---%@----%p",p2,&p2);
    }
    return 0;
}
Copy the code
2021-06-17 16:00:37.697384+0800 memory offset [12536:388334] --<LWPerson: 0x600002000000>----0x7ffee357bc70 2021-06-17 16:00:37.697465+0800 Memory offset [12536:388334] --<LWPerson: 0x600002000010>----0x7ffee357bc68Copy the code
  • allocOpen up internal existenceThe heap area.A local variableOpen up internal existenceThe stack area
  • The heap arealowAddress — — >highAddress, stack areahighAddress — — >lowaddress

The illustration below

Pointer to an array

Int main(int argc, char * argv[]) {@autoreleasepool {int c[4] = {1,2,3,4}; int *d = c; NSLog(@"%p - %p - %p ",&c,&c[0],&c[1]); NSLog(@"%p - %p - %p ",d,d+1,d+2); for (int i = 0; i<4; Int value = *(d+ I); int value = *(d+ I); NSLog(@"value = %d",value); } } return }Copy the code
2021-06-17 16:32:13.132035+0800 Memory offset [1299:415236] 0x7ffEEBfd9C80-0x7FfEEBfD9C80-0x7FfEEBfD9c84 2021-06-17 16:32:13.132122+0800 Memory offset [12919:415236] 0x7FfEEBfd9C80-0x7FfEEBfD9C84-0x7FfEEBfD9c88 2021-06-17 16:32:13.132206+0800 Memory offset [12919:415236] value = 1 2021-06-17 16:32:13.132287+0800 Memory offset [12919:415236] value = 2 2021-06-17 16:32:13.132341+0800 MEMORY offset [12919:415236] value = 3 2021-06-17 16:32:13.132418+0800 memory offset [12919:415236] value = 4Copy the code
  • The address of an array is the element in the arrayThe first address, i.e.,&cand&c[0]Are allThe first address
  • Between each element of an arrayAddress spaceBased on the current elementThe data typeDecision of the
  • The addresses of the elements of the array can be passedThe first address+n*The type sizeIn which the elements in the array must be of the same type.
  • Array elements are differentThe first address+The offsetMethod, based on the offset value of the current variable (requires the addition of the previous type sizes)

The illustration below

conclusion

  • A memory address is a memory elementThe first address
  • Memory offset can be based onThe first address+ Offset valueMethod to get the address of the relative variable

Class analysis (isa)

When exploring the object, it is found that the OBJECT’s ISA points to the class. Everything is an object, a class is an object, and there is an ISA in a class. What class does isa refer to? This class is a metaclass defined by Apple.

The metaclass

Metaclasses are automatically generated by the compiler. Why would Apple automatically generate such a class? So let’s explore

Memory number of class objects

int main(int argc, char * argv[]) {
    @autoreleasepool {
       Class class1 = [LWPerson class];
       Class class2 = [LWPerson alloc].class;
       Class class3 = object_getClass([LWPerson alloc]);
       Class class4 = [LWPerson alloc].class;
       NSLog(@"\n-%p-\n-%p-\n-%p-\n-%p-",class1,class2,class3,class4);
    }
    return 
}

Copy the code
-0x100009510-
-0x100009510-
-0x100009510-
-0x100009510-
Copy the code

Source code analysis: class object address is the same, each class in memory only a piece of memory, and ordinary objects have obvious differences

The metaclass inquiry

The LWPerson class has two different addresses, but a class object has only one address. 0x0000000100009500 is the class address of LWPerson, so 0x00000001000094d8 is the class address that Apple calls metaclass

conclusion

  • The metaclassIt is automatically created by the system compiler and has nothing to do with the user
  • The object’sisaPoint to theclass, class objectisaPoint to theThe metaclass
  • Of the classThe name of the classThe class associated with it isThe metaclasstheThe name of the classIs the same (only the associated metaclass has a class name)

Isa bitmap

object.class.The metaclassThere areisa, and the specificisaHow to go about the process. Create an objectNSObject * obj = [NSObject alloc]And combined with thelldbExplore the

  • NSObjectThe object ofobjtheisa –> NSObjectclass
  • NSObjectOf the classisa–> A metaclass
  • A metaclasstheisa–> A metaclass(To himself)

Customize the LWPerson class, create an object LWPerson * person = [LWPerson alloc], and explore with LLDB

The metaclass isa points to the root metaclass, and the question is: why not NSbOject? Notice that the NSbOject class address is not the same as the root metaclass address, so it points to the root class

  • LWPersonThe object ofpersontheisa –> LWPersonclass
  • LWPersonOf the classisa–> LWPersonOf the classThe metaclass
  • The metaclassisa–> A metaclass

isaPosition flow chart

conclusion

  • The object’sisaPoint to theClass object
  • Class objectisaPoint to theThe metaclass
  • The metaclassisaPoint to theA metaclass
  • A metaclassisaPoint to theA metaclass(To himself)

Inheritance diagram of class, metaclass, root metaclass

First create a LWTeacher class to inherit from LWPerson and explore the inheritance relationship between them. The following code

int main(int argc, char * argv[]) { @autoreleasepool { Class tMetaClass = object_getClass(LWTeacher.class); Class tMetaSuperClass = class_getSuperclass(tMetaClass); Class pMetaClass = object_getClass(lwPerson.class); Class pMeatSuperClass = class_getSuperclass(pMetaClass); Class pMeatSuperClass = class_getSuperclass(pMetaClass); Class nMetaClass = object_getClass(nsobject.class); Class nSuperClass = class_getSuperclass(nsobject.class); Class nMetaSuperClass = class_getSuperclass(nMetaClass); //NSObject Class nMetaSuperClass = class_getSuperclass(nMetaClass); NSLog(@"LWTeacher-%p", lwteacher.class); // NSLog(@"LWTeacher-%p", lwteacher.class); NSLog(@"LWPerson-%p",LWPerson.class); NSLog(@"NSObject-%p",NSObject.class); NSLog(@"%@ - %p - %@ - %p",tMetaClass,tMetaClass,tMetaSuperClass,tMetaSuperClass); NSLog(@"%@ - %p - %@ - %p",pMetaClass,pMetaClass,pMeatSuperClass,pMeatSuperClass); NSLog(@"%@ - %p - %@ - %p",nMetaClass,nMetaClass,nMetaSuperClass,nMetaSuperClass); } return }Copy the code
2021-06-17 22:12:45.737286+0800 testClass[15587:581908] LwTeacher-0x100009618 2021-06-17 22:12:45.737359+0800 TestClass [15587:581908] LWPerson-0x100009528 2021-06-17 22:12:45.737396+0800 testClass[15587:581908] NSObject- 0x7ffF8deb3118 2021-06-17 22:12:45.737434+0800 testClass[15587:581908] LWTeacher - 0x1000095f0 - LWPerson - 0x100009500 2021-06-17 22:12:45.737497+0800 testClass[15587:581908] LWPerson - 0x100009500-nsobject - 0x7ffF8deb30F0 2021-06-17 22:12:44.737540 +0800 testClass[15587:581908] NSObject - 0x7FFF8DEB30f0-nSObject - 0x7FFF8DEB3118 2021-06-17 22:12:45.737583+0800 testClass[15587:581908] - - (null)Copy the code

Source code analysis of NSObject’s superclass prints nil. The parent class of the metaclass of LWTeacher is the metaclass of LWPerson (the address of the metaclass of LWPerson is different from the address of the class of LWPerson). The metaclass of LWPerson is the metaclass of NSObject, and the metaclass of NSObject is the metaclass of NSObject (same address as NSObject)

  • LWTeacherinheritanceLWPerson.LWPersoninheritanceNSObject.NSObjectIs the parent classnil
  • LWTeacherYuan class inheritanceLWPersonMetaclasses,LWPersoninheritanceA metaclass.A metaclassinheritanceNSObject

Flow diagrams of inheritance between classes

Isa bitmap and inheritance map

Apple officially provides the ISA bitmap and inheritance map

Class structure analysis

When exploring the object nature of the underlying principles of IOS & ISA association Class, isa is found to be of type Class. The Class type is objc_class *, and objc_class is a structure. All the underlying implementations of classes are objC_class. Search globally for objc_class in objC4-818.2 as follows

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; // OBJC2 is not availableCopy the code

Now, normal is OBJC2, and objc_class defined above is not available in OBJC2

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 flagsCopy the code

Objc_class inherits objC_object, objc_object has only one member variable isa, isa is specific function has been explored. How to explore the following three member variables whose specific functions are unknown? The address of the class is known, so get the address of the member variable in the class based on the first address + offset value explored above, and then get the value. But the offset value needs to know the size of all member variables before the current variable

  • isaStructure pointer occupied8 bytes
  • Class superclassIt’s also a pointer to a structure8 bytes
  • cache_t cacheIs the size of the structure type, determined by the member variables in the structure
  • class_data_bits_t bitsIf you know the size of the first three member variables, you can getbitsThe address of the

All of objc_class’s member variables can be mapped to their addresses as long as they know the cache_t memory size. Let’s explore the cache_t memory size

typedef unsigned long uintptr_t; #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 { private: explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8 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; / / 8}; // Here are some methods omitted};Copy the code

Cache_t is a struct type with two member variables, _bucketsAndMaybeMask, and a union

  • The _bucketsAndMaybeMask is uintPtr_t unmarked long integer which takes up 8 bytes

  • There are two member variable structures and _originalPreoptCache in the union. The size of the union is determined by the maximum variable type in the member variable

  • The _originalPreoptCache is an 8-byte structure pointer

  • We have _maybeMask, _flags, _occupied. The maybemask is uint32_t 4 bytes, the flags and the occupied are 2 bytes each and the structure size is 8 bytes

  • Cache_t has a memory size of 8+8 or 8+4+2+2 which is 16 bytes

Explore each member variable in the class. The memory address of the member variable is as follows

  • isaIs the memory address ofThe first address“Has been explored in previous blogs
  • superclassIs the memory address ofThe first address+0x8
  • cacheIs the memory address ofThe first address+0x10
  • bitsIs the memory address ofThe first address+0x20

Today we will explore the member variable bits and what important information is stored in bits

Bits.data () should store data. The type of data() is class_rw_t*

Class_rw_t is a struct type that provides a method to get a list of attributes, a list of methods, and a list of protocols. To verify that methods, properties, and variables are in class_rw_t, add properties, methods, and member variables to the LWPerson class

Attribute to explore

P $7. Get (index) gets the corresponding property, which is stored in preperty_list_t

p $7.get(2)Error array out of bounds, property list only2Attributes, but declared variablesheightWhere is it stored?

At the end of the paper, the variable exploration module under the module is supplemented, and the storage of variables has been explored

Methods to explore

  • Object methodsStored in theLWPersonClassmethod_list_tIn the
  • Class methodNot in theLWPersonMethod listmethod_list_tIn,Class methodWhere should I put it?
  • p $7.get(index)You can’t get a specific value in the method list becausemethod_tIs processed throughbit()Get variables

At the end of the article, the class method exploration module is added under the module, and the storage of the class method has been explored

conclusion

There are isa, superclass, CHche and BITS member variables in the class. In the process of exploring bits, we find that bits stores the familiar attribute list, method list, member variable list, protocol list, etc., which opens our understanding of the class. We will continue to explore the class later.

supplement

Variable to explore

There is no variable stored in the class attribute list. Variables are user-defined in the class and should not be placed in the metaclass. Observe that class_rw_t also has a method to obtain class_ro_t *

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; void *baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; Const uint8_t * weakIvarLayout; // Uint8_t * weakIvarLayout; property_list_t *baseProperties; // This field exists only when RO_HAS_SWIFT_INITIALIZER is set. _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0]; };Copy the code

Class_ro_t is a struct type with an ivar_list_t * ivars variable. Is a list of variables that logically should be stored.

  • variableThe underlying implementation of isivar_t. Stored in theclass_ro_tIn the variable list
  • The system will giveattributeAutomatically generates a tape_ the property nameVariable, stored inclass_ro_tIn the variable list

Class method exploration

Methods of an object are stored in a class, so class methods might be stored in a metaclass. Follow this line of thinking

Class methods are stored in a list of methods in the metaclass