After the OC file is compiled, the class-related data structures remain in the object file and are parsed and used at run time. When the application is running, the information of the class is loaded and initialized. This process involves the two class methods of the class: Load and Initialize. Let’s take a look at the differences between the two methods. (First of all, these two methods are system calls, developers generally do not actively call the two methods, there is no point in doing so, so the rest of the explanation is for system calls, not active calls).
1. The load method
1.1 Call Timing
When we start the program, to participate in the compilation of class, the classification will be loaded into memory, the load method is invoked when the class is loaded (if the class has to realize the load method), this process has nothing to do with this class is used, that is to say, if you have a class (MyClass) even in the whole program is not used, There isn’t even a file to import the MyClass header, so the MyClass load method is called as well. The program’s main function is called after all classes and classes are loaded into memory, so the load method of all classes is called before the main function. And the load method for each class and category is called only once.
1.2 Call Sequence
If all classes and classes in a program implement load methods, all load methods will be called. Their execution order follows the following rules:
- Execute all classes first
load
Method, and then execute all classifiedload
Methods. - The implementation of the class
load
Method, the first compiled class is executed in the order in which it was compiled, but if a class inherits from another class, the parent class is executed firstload
Method before executing its ownload
Methods. - Performing classification
load
Method is compiled in the order in which categories are involved, with the first compiled category being executed first.
Build Phases –> Compile Sources (the top one is the first one to Compile, and we can drag the file to change the Compile order).
Here’s an example of the order in which the load methods are executed. The Person class has two classes: AAA and BBB. The men class has two classes: CCC and DDD. The Book class has nothing to do with the previous classes.
Explanation:
- The compilation order is from top to bottom, with the top compiled first and the bottom compiled later. Since the class is executed first
load
And then perform the classificationload
, the first class to participate in compilation ismen
And themen
Inherited fromPerson
, so execute firstPerson
theload
(thoughPerson
Is post-compiled, but it is the parent class, so it is executed first), and then executedmen
theload
. The next thing that’s involved in compiling isBook
Class, so execution followsBook
theload
. The next class involved in compiling isPerson
Because of itsload
The method has already been executed and will not be executed. - All of the classes
load
Methods are executed after the start of the classificationload
, the order in which categories participate in compilation ismen+ccc
–>Person+aaa
–>men+ddd
–>Person+bbb
So it’s classifiedload
Methods are also executed in this order.
1.3 Execution Mode
We know that when a class has a method with the same name as the class, calling that method ends up executing the method in the class. The Person class and each of its classes have a load method called, but the Person class and each of its classes have load methods called.
This is because the load method is not called in the same way as a normal method. A normal method call is implemented through the message sending mechanism. It will first look up the method list of the class or metaclass. If it finds a method, it will execute.
When a load method is called, each class is directly called based on the address of the load method, rather than the objc_msgSend method lookup process. This means that a class implements a load method and does not execute it if it does not (and does not look up the parent class if it does not).
For a more detailed overview of the underlying implementation process, check out objC4’s source code. Here is the related function call process for reading the source code: Start with the _objc_init function in objC-os. mm –>load_images–>prepare_load_methods–>schedule_class_load–>add_class_to_loadable_list — ->add_category_to_loadable_list–>call_load_methods–>call_class_loads–>call_category_loads–>(*load_method)(cls, SEL_load).
1.4 What should I pay attention to when implementing the Load method
We usually do Method Swizzle in the Load Method, but otherwise, we try not to write code in the Load Method unless we really have to, especially not to use other classes in the Load Method, because at this point the other classes may not have been loaded into memory, and using them arbitrarily can be problematic.
If you do write some code in the load method, try to keep it as simple as possible and don’t do time-consuming or lockwaiting operations because the entire program blocks while executing the Load method, which can take too long to start or even fail to start.
2. The initialize method
2.1 Call Timing
The Initialize method is called when the class or one of its subclasses receives the first message. In this case, the message is the instance method or class method call, so all initialize calls are made after main. And the Initialize method is only called once by a class. Unlike the load method, the Initialize method will not be called if a class has not been used during the program’s execution.
2.2 Invocation Mode
The initialize method calls follow the same objc_msgSend process as normal method calls. So if a class and its classes implement the Initialize method, it will end up calling the methods in the class.
If the initialize method is implemented by both the subclass and its parent class, the parent class will be called first, and then the subclass will be called. By looking at the source code, we know that it is the initialize method of the parent class that is actively called during the underlying implementation.
Here’s an example:
PersonSub1 and PersonSub2 also implement Initialize. PersonSub3 and PersonSub4 do not implement initialize. Instantiate objects in the following order:
PersonSub1 *ps1 = [[PersonSub1 alloc] init]; Person *person = [[Person alloc] init]; PersonSub2 *ps2 = [[PersonSub2 alloc] init]; PersonSub3 *ps3 = [[PersonSub3 alloc] init]; PersonSub4 *ps4 = [[PersonSub4 alloc] init]; / / * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * 2020-01-06 15:52:38. 429218 + 0800 CommandLine [68706-7207027] + [Person the initialize] 2020-01-06 15:52:38.429250+0800 CommandLine[68706:7207027] +[PersonSub1 initialize] 2020-01-06 15:52:38.429287+0800 CommandLine[68706:7207027] +[PersonSub2 initialize] 2020-01-06 15:52:38.429347+0800 CommandLine[68706:7207027] +[Person Initialize] 2020-01-06 15:52:38.429380+0800 CommandLine[68706:7207027] +[Person initialize]Copy the code
The initialize method is called three times. The initialize method is called three times.
Here we need to explain the execution process of the underlying source code. Each class has a mark to record whether the class has called Initialize. I use a BOOL isInitialized to represent it, and then use selfClass to represent my own class and superClass to represent the parent class. Here I use pseudocode to describe the underlying source code execution process:
// Execute the code inside initialize if you have not already called itif(! selfClass.isInitialized){if(! Superclass.isinitialized){// Send a message to the parent class if initialize has not been performed. objc_msgSend(superClass,@selector(initialize)); SelfClass,@selector(initialize);} // initialize a message to your own class. If initialize succeeds, set isInitialized to YES. }Copy the code
Let me explain the results of the above operation according to this process:
- First of all,
PersonSub1
Being used, while at this timePersonSub1
theisInitialized
Is NO and superclassPerson
theisInitialized
Is also NO, so a message is sent to the parent class firstinitialize
After the execution is completePerson
theisInitialized
A YES. thenPersonSub1
Execute one’s owninitialize
After the execution is completePersonSub1
theisInitialized
A YES. So this is the first step to print+[Person initialize]
And then print+[PersonSub1 initialize]
. - And then the
Person
Instantiate, at this pointPerson
theisInitialized
Is YES, so it will not be called againinitialize
. So I didn’t print anything at this step. - Then there is
PersonSub2
Instantiate, at this pointPersonSub2
theisInitialized
Is NO, the parent classPerson
theisInitialized
Is YES, so onlyPersonSub2
Will performinitialize
After the execution is completePersonSub2
theisInitialized
A YES. So what this step prints is+[PersonSub2 initialize]
. - then
PersonSub3
Instantiate, at this pointPersonSub3
theisInitialized
Is NO, the parent classPerson
theisInitialized
Is YES, so onlyPersonSub3
Will performinitialize
But because ofPersonSub3
Do not implementinitialize
, it goes to the parent class to find the implementation of the method, and when it finds it, it executes the parent classPerson
theinitialize
(Notice here isPersonSub3
performPerson
In theinitialize
Rather thanPerson
Executed), after executionPersonSub3
theisInitialized
A YES. So what this step prints is+[Person initialize]
. (Note that method information is printed here, indicating that it is executedPerson
In theinitialize
“Instead of saying yesPerson
Of the callinitialize
). - Finally,
PersonSub4
Instantiate, this step is the same as the previous step, after executionPersonSub4
theisInitialized
A YES. This step is printing+[Person initialize]
.
The result is that Person, PersonSub1, PersonSub2, PersonSub3, and PersonSub4 have all been initialize once. The next two calls are for PersonSub3 and PersonSub4.
2.3 Precautions for Use
Although it is safer to use initialize than load (since all classes have already been loaded into memory by the time initialize is called), we should use initialize as little as possible. We should be especially careful to implement initialize in classes, because if it is implemented in classes, The Initialize method implemented by this class will not be called. In practice, the initialize method is used to initialize global or static variables.