Contents of this Chapter:

The essential structure of classes, and the relationship between classes and metaclasses. Where to store class attributes, member variables, instance methods, class methods, etc.

Conclusion:

1. The memory of the class and metaclass is already allocated after compilation, and only one copy is available;

2. The class isa -> metaclass ISA -> root metaclass, and the root metaclass ISA points to itself. The root metaclass inherits from its own class. For example, the metaclasses of NSObject and NSProxy are inherited from itself. The root class inherits from nil;

3. The essence of classes and metaclasses is an objc_class structure. Its member variables are isa(8), superClass(8), chache(cache_t 16 bytes), and bits(class_data_bits_t 8 bytes) from top to bottom.

4. Member variables, attributes, and instance methods of a class can be found by using bits. Class methods are stored in metaclasses and can be found using bits. Class attributes and instance methods can be found using bits to find class_rw_t (rW can be read and written, represents the structure that can be changed at runtime, dirty memory). Class_ro_t (ro read-only, meaning that the structure has been determined at compile time and cannot be modified at run time). As for the partition these two are optimized clean Memory by WWDC2020runtime) find its members baseProtocols, IVars, basePropertyies, function baseMethods(), etc. The member variables of the class are found in ro

5. Look at the following figure, get: object ISA -> class, class ISA -> metaclass, metaclass ISA -> root metaclass, root metaclass ISA -> root metaclass itself; SubClass -> SuperClass -> RootClass -> nil; Then you can get the metaclass inheritance: SubClass(meta) -> SuperClass(meta) -> RootClass(meta) -> RootClass -> nil

Explore the essential structure of classes and the relationship between classes and metaclasses

To explore the basic structure of classes, we’ll start with an example of the class Person and its subclass, Manger, shown below. Person *p1 = [[Person alloc] init]

First tell the result the contents of its Person class, then look at objc source code for verification: 1. Class isa, 2.superClass pointer address 3.cache, 4. bits.And let’s be clear about this

Person *p1 = [[Person alloc] init]; Person *p2 = [[Person alloc] init]; NSLog(@"----%@-----%p", p1, &p1); NSLog(@"----%@-----%p", p2, &p2); ----<Person: 0x101A06450 >----- 0x7ffeefBFF408 ----<Person: 0x101A064B0 >----- 0x7ffeefBff410Copy the code

&p1 (0x7FFeefBFF408, pointer to a pointer) -> p1 (0x101A06450), P1 isa -> class.

View the memory structure of the class against objc

We already know that objects are essentially objc_object structures when we move from clang to c++ low-level code. But we find out that the isa of an object is pointing to the address of its Class and we find out that the address of the Class is actually going to have a result, and in our OC level that Class also stands for Class, and Class is actually a pointer to an objc_class * struct, do we know that the Class is essentially an objc_class struct, And it inherits from the objc_object structure.

Explore the relationship between classes and metaclasses

1. Continue to verify with LLDB, and discover that the memory address pointed to by the Person class isa output is still a Person. I guess that proves what we thought.

2. In the Person metaclass address 0x0000000100008680, isa points to the NSObject metaclass 0x000000010036A0F0

3. Then explore the NSObject metaclasses 0x000000010036A0F0

Summary: Get the above summary 2, 3, 5

Sub-summary: This chapter does not explain, but you can verify class inheritance yourself: Manger -> Person -> NSObject -> nil; Metaclass inheritance: Manger -> Person -> NSObject -> (root metaclass itself)

Explore where the class attributes, member variables, instance methods, and class methods are stored

Explore the class example: Get properties, member variables, and instance methods from the Person class

Validation: This content is brief, if necessary, you can see the objC source code in detail, just say the general process and verification method

1. Get the Person class

Get its bits. The position of the bits pointer is Person’s isa + 0x20(32 bytes). See the structure objc_class for why. It has the last member bits so it is: class address + isa size (8 bytes)+ Superclass size (8 bytes)+ Cache size (16 bytes)

2. Obtain the class_rw_t structure

Objc_class internal methodsclass_rw_t *data()Get the class_rw_t structure

3. Obtain attributes

Call the class_rw_t methodconst property_array_t properties() const

4. Obtain the instance method

Call the class_rw_t methodconst method_array_t methods()

5. Obtain member variables

Call the class_rw_t methodconst class_ro_t *ro() constGet the structure class_ro_t and print its member ivars

6. Get the class method

The getclass method is the same as 4 except that you need to use a metaclass to get it.

7. Obtain protocol

Since this is a follow-up, it is different from the above to only show the example method.

Validation:

Pay attention to: Because it is given a protocol_ref_t, it is actually a protocol_t * structure pointer address. So we’re going to do a strong turn

1. First look at the struct protocol_list_t, we need to get the list[0] first

2. The LLDB is forcibly transferred

3. View the protocol_t output.

  • MangledName: indicates the address of the protocol
  • Protocols: represents the address of the inherited protocol (verified as NSObject, which inherits from nil). Out of thin Air
  • InstanceMethods: Instance method of the protocol
  • ClassMethods: classMethods of the protocol
  • OptionalInstanceMethods: An optional instance method for a protocol
  • OptionalClassMethods: optionalClassMethods for the protocol
  • InstanceProperties: instanceProperties
  • _classProperties: Class properties

supplement

Why does Apple divide the class structure into RW, ro, and RWE?

Because of the runtime mechanism, we might insert methods, attributes, etc. into the class at runtime, which is a huge waste of memory. So WWDC2020 published the results, stacked together with all the previous ones, divided into rW (class_rw_t), ro (class_ro_t), and RWE (class_rw_ext_t). Apple respects the less dirty, the more clean, the better. Clean Memory can be reloaded from the hard disk, but dirty Memory is run time data, which cannot be easily reclaimed. Once recycling means nothing. So clean Memory is usually recycled, but if dirty Memory is too big, it is not comfortable with him.

Rw: Readable and writable (dirty Memory), where users can insert method properties at run time. Memory changes at run time. But RW includes ro and RWE

Ro: Clean Memory. When a class is loaded, its member variables, properties, and methods are here, and runtime changes are not affected

Rwe: Extensible, meaning extra information about a class, but in practice, only a few classes change their content, avoiding consumption.