The opening
Study hard, don’t be impatient, make progress a little bit every day.
In the last two articles, you’ve seen the message sending process for objc_msgSend; It can be summarized in four simple steps:
Message sent ——> find IMP ——> Cache hit ——> message callback;
And this series of implementation basis, is basically in the function method has been implemented in the case, if the function method is not implemented, direct call, the application will crash, prompt method can not be found, so in the case of the method is not implemented, how to achieve dynamic message, prevent program crash?
Source search ideas
Create a class and declare an object method, but the method is not implemented. When the program runs, an error message is displayed and the error message is printed:
Let’s take a quick look at how the error message appears:
In the imp slow lookup process, the default value assigned to imp is __objc_msgForward_impcache. In the assembly source code, the execution process is as follows:
1, __objc_msgForward_impcache
2, __objc_msgForward
3, __objc_forward_handler
Search for the __objc_forward_handler function, find the method exists only in the assembler source code, but the error message is in C/C++, so need to transform the query mode, objc_forward_handler directly search for the function name, in C/C++ source code, find the error message source code:
In many cases, it is very difficult to view the source code, we need to calm down, patience bit by bit analysis, pondering, eventually will have a harvest;
Dynamic methods
Back to the IMP search process, when the IMP can not find, the direct program crash, that is very bad experience, and apple engineers, in order to show their own awesome, but also give us a chance to tolerate the fault; Then come to see, cattle force coax coax people, tolerance of wrong thinking is how it is.
In the slow lookup function lookUpImpOrForward, after the for loop ends, the imp executes the following logic if it is not found:
PS: Why the if condition is executed only once in a single process will be explained later.
Send dynamic method
Analyze the dynamic message sending process at resolveMethod_locked:
resolveMethod_locked
resolveInstanceMethod
The resolveInstanceMethod method is determined by the class, so it’s a class method, not an object method;
Therefore, when the IMP cannot find, the system will send the resolve_sel message to query whether the class implements the resolveInstanceMethod method.
When the program executes the resolveInstanceMethod method, we find that the system executes the resolveInstanceMethod twice before executing the object method. This means that if you assign sel to the resolveInstanceMethod method in advance, you can avoid a crash.
Next, implement the resolveInstanceMethod class method and dynamically add the method;
There is no sayHi method, we try to replace the method, let sayHi method, execute the logic of sayNB, program execution is no problem.
Class method dynamic resolution
The system sends different methods according to whether the CLS is a metaclass. CLS is a metaclass that executes resolveClassMethod.
ResolveClassMethod is a class method that’s written in the metaclass;
In metaclasses, class methods exist in the form of object methods, so the object methods of metaclasses are the class methods of classes.
In the current class, practice resolveClassMethod:
Implement resolveClassMethod, transform the unimplemented sayHappy method into sayKC, so as to avoid system exception;
After the resolveClassMethod is executed, it continues to determine whether the lookUpImpOrNilTryCache exists, and then executes the resolveInstanceMethod method. Why does the system send the object dynamic message again here?
Remember the ISA bitmap, in the ISA bitmap, the inheritance chain of class, subclass –>superclass –> root class, so the method of class, is also consistent with its inheritance chain;
A class method, on the other hand, is stored in a metaclass, so it can execute both the inheritance chain of a class and the inheritance chain of a metaclass as an object within the metaclass. So there are two cases of execution;
- Through two examples, we know that all the methods in the class will be executed
resolveInstanceMethod
Methods, and methods inside the metaclass, are executedresolveClassMethod
Method, so if we create a metaclass of NSObject, implementresolveInstanceMethod
Method, is it possible to call NSObject’s fault-tolerant handling when all of the class’s methods don’t get it? Let’s put it into practice:
And the result is just as we expected, no problem, no problem;
summary
When the IMP cannot be obtained through SEL, the system provides a dynamic remedy. If the app implements the remedy, the system will not crash. With the pattern of NSObject classification, you can listen to all the methods in the project; If there is a standard method name, the method name can be used to distinguish between modules. Hook records can be passed to the background monitor as log files.
When the Runtime runtime mode is used to dynamically dispatch methods, it is easy to see that the method IMP does not exist. So using hook thinking to develop is a kind of AOP aspect oriented programming way of thinking;
And we often use the research and development way, is OOP object-oriented programming way; OOP object oriented programming all objects between the division of labor is very clear, advantage is that the coupling is very small, but easy to cause redundant code, code redundancy will extract the encapsulation, generate public class, lead to business to the public class has a strong dependence, strong coupling, it is not in conformity with the thinking in architecture, architecture hope high cohesion and low coupling;
So in order to reduce intrusion into business code, using runtime dynamic distribution, injecting methods into code, for example, into NSObject class, resolveInstanceMethod, has no effect on the methods in QLYPerson class; This is quite into the whole aspect, this is AOP aspect oriented programming way of thinking;
Disadvantages of AOP’s aspect oriented programming approach:
- 1, AOP thinking for beginners, is more difficult to understand, most people are not easy to form this kind of thinking;
- 2. As in the above example, there will be more in the NSObject category
if
The integrated judgment condition has certain performance consumption; - 3. Under classification
resolveInstanceMethod
It will be executed multiple times, with a certain performance cost; - 4,
resolveInstanceMethod
In front of the message forwarding mechanism, if you do one-size fits all program logic here, then the subsequent remedial logic of the system will not be implemented;
Therefore, it is not very friendly to operate AOP at this level, so you should operate AOP logic in the message forwarding process;
Viewing log Files
If the dynamic method resolution is not implemented, the system will determine if IMP is equal to the default value, if so, return nil, the empty IMP, and then execute the done process below, and do any business supplemental logic for IMP. What other way to remedy this?
In the done process, the log_and_fill_cache log writing service is performed, and logs are recorded in the local TEM file by using the logMessageSend function. Can we view the log file to observe the subsequent execution process of the program?
The breakpoint does not enter the logMessageSend, indicating that the log file is not opened. The fields to open the log file are: objcMsgLogEnabled; Tracking source, locating the inside instrumentObjcMessageSends function, assignment of objcMsgLogEnabled next in the program, add log open constants:
支那extern* * * *void支那instrumentObjcMessageSends(**BOOL** flag);
Copy the code
Before calling an object method, enable log writing:
LGPerson *person = [LGPerson alloc];
instrumentObjcMessageSends(**YES**);
[person sayHello];
instrumentObjcMessageSends(**NO**);
Copy the code
Go to the logMessageSend function, open Finder, command + Shift + G, go to the folder, and find the file information:
Go to the folder, there is an msgSends file inside, which is the log file :(note that if you start writing to the msgSends in the source code, there is no log file in the log file and you need a new project to view the log file)
Through the log file you can see, the program in addition to performing resolveInstanceMethod, also performs the forwardingTargetForSelector, methodSignatureForSelector these two functions:
So what do these two functions do? How should it be used? Wait for the follow-up analysis……