This is the seventh day of my participation in the August More text Challenge. For details, see: August More Text Challenge

loadandinitializeMethod invocation principles and order?

The load method

  • loadThis is done during the application loading processmainDelta before delta, delta before deltadyldTo complete the call during loading;
  • In the underlyingload_imagesWhen the method is called, two tables are maintained, one for storageclasstheloadMethod, and the other one for storageclassificationtheloadMethods;
  • Priority callsclasstheloadMethods;
  • In the classloadMethod, make a recursive call to ensureThe parent classCan be prioritized;
  • loadMethods are called in the following order:The parent class,A subclass,classification;
  • In the classificationloadMethod invocation order is determined by mutation order;

The initialize method

  • initializeIs called when the first message is sent, soloadMethods thaninitializeMethod is called first;
  • Classification ofinitializeThe method will be inserted first to make sure it’s sortedinitializePriority call;

C++ constructor calls

  • indyldWhen analyzing the process, we know that the call order is:load,C++ constructors,main;
  • If it’s a source code projectobjcIn theC++ constructorsIn theobjc_init()“Will passstatic_initMethod call firstC++Constructor;

runtimeWhat is?

  • runtimeIs made up ofCandC++Assembler implements a set of apis forOCLanguage has been addedobject-orientedandThe runtimeThe function;
  • Runtime refers to the delay of data type determination from compile time to run time; Such asThe extension and the categoryThe difference between;
  • Everyday writtenOCThe code, as the program runs, actually ends up being converted toRuntimetheCLanguage code,RuntimeisObject-CA behind-the-scenes worker;

What is the nature of the method?selWhat is?impWhat is? How were they related before?

The essence of the method is to send a message, and the process is as follows:

  • 1. Quick search (objc_msgSend) ~cache_tCache messages;
  • Slow lookup ~ recurse yourself | superclass ~lookUpImpOrForward;
  • Message not found: dynamic method parsing ~resolveInstanceMethod;
  • The message is forwarded quickly ~forwardingTargetForSelector;
  • The message is forwarded slowly ~methodSignatureForSelector & forwardInvoation;

Sel is the method number that is compiled into memory during read_images; Imp is the pointer to the function implementation, imp is to find the process of finding the function; For example, SEL is the table of contents of a book, and IMP is the page number of the book. Looking for specific functions is looking for specific chapters in the book; 1. What do we want to see? Sel 2. Find the page number according to the catalog; Imp 3. Go to the content page.

Is it possible to add instance variables to the compiled class? Can you add instance variables to classes created at run time?

  • You cannot add an instance variable to a compiled class
  • Instance variables can be added as long as the class is not registered in memory

Reason: Compiled instance variables are stored in ro, which is read-only. Once compiled, the memory structure is completely determined and cannot be modified. Properties and methods can be added;

[self class]and[super class]The difference and principle analysis of?

We create a Person class and a subclass Teacher; Then print the following in Teacher’s init method:

- (instancetype)init {
    self = [super init];
    if (self) {
        NSLog(@ % @ = = % @ ""[self class], [super class]);
    }
    return self;
}
Copy the code

To view the print:

According to our normal understanding, one Teacher and one Person should be printed, but why are two teachers printed?

Next, let’s look at the implementation of the class method:

So the final call is going to be objc_getClass(self), where self is passed from the hidden argument (ID self, sel _cmd), which is the message sent objc_msgSend(ID Receiver,…) So when you call [self class] the self in objc_getClass(self) is Teacher;

Next, look at the implementation of objc_getClass:

The implementation is to return the ISA of the class, because the ISA of Teacher is Teacher, so return [self class] print: Teacher;

So why does [super class] print Teacher? Let’s look at teacher.m’s CPP file through clang:

We find objc_msgSendSuper is called from the bottom, we look at objc_msgSendSuper in the source project:

OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0.2.0.9.0.1.0.2.0);
Copy the code

The first argument is objc_super, and we look at the structure:

Super_class is the first Class to search. Super_class is the first Class to search. Super_class is the first Class to search. Then the whole invocation logic is clear:

Calling [super class] in Teacher calls the objc_msgSendSuper method at the bottom with the parameter receiver being Teacher, and super_class is the first class to look for, which is Teacher’s superclass Person; Because the receiver of [super class] is Teacher, the final print is Teacher;

When it calls objc_msgSendSuper, the final call at the bottom is objc_msgSendSuper2

Translation memory

Let’s look at the result of a code execution:

Both methods can call the saySomething method. Why?

The first is understandable, so why is the second also possible?

To solve this problem, we first need to understand why person can call the saySomething method.

The reason that Person can call saySomething is because we can find the corresponding class through the isa of the Person object, shift the address in the class to find cache_t, and then look up the method;

Here the kc pointer points to the first address of class LGPerson, so you can still find the method by translating it in memory;

Next, let’s modify the printing of the saySomething method:

- (void)saySomething{
    NSLog(@"%s - %@",__func__, self.kc_name);
}
Copy the code

Run to see the printed result:

[person saySomething] prints null because the kc_name attribute is not assigned; But why is [(__bridge ID)kc saySomething] printed as a Person object? Although they both call saySomething, the results are different, indicating that there is a difference between person and KC.

Person is an object that opens up memory space where isa and member variables are stored, whereas KC is just a pointer address and has no memory space. As follows:

When the person accesses kc_name, the person accesses kc_name by translation, i.e. self+ the offset of kc_name to find the address of kc_name, and then obtains the value. Select * from person where kc_name is located; select * from person where kc_name is located;

So let’s modify the code to verify:

So let’s see what’s the address of KC offset by 8?

So I’m printing a Person object;

Next, we modify the code as follows:

Above the kc_name attribute, add another attribute, kc_hobby, and run print:

If kc_name is an attribute, person must be offset by 8. If kc_name is an attribute, person must be offset by 16 (0x10).

So how does KC find the ViewController object after the offset 0x10?

To solve this problem, first we need to analyze the stack logic, according to the previous conclusion, we know that [super viewDidLoad] here is also a structure, then how structure is substack? Let’s test it out from the definition structure;

Customize a structure:

struct structT{
    NSNumber *num1;
    NSNumber *num2;
} structT;
Copy the code

Modify the code as follows:

Print the memory address, you can analyze the stack memory as follows:

Take a look at where the structure data is stored:

Num1 is in the lower address, num2 is in the higher address.

So back to our original code:

[super viewDidLoad] [super viewDidLoad] [super viewDidLoad] [super viewDidLoad] [Super viewDidLoad] [Super viewDidLoad] [Super viewDidLoad] [Super viewDidLoad] [Super viewDidLoad] [Super viewDidLoad]

{num1, num2} structure stack, num1 at the low address, num2 at the high address

So how does the parameter stack?

(id p1, ID p2) parameter push, p1 at the high address, P2 at the low address

Next, verify our conclusion with a test code: