The previous article, Application Loading, looked at the application loading process, the startup relationship between dyld and objc_init, and the call timing of map_images and load_images. In this article, we need to address some of the remaining issues and expand on the previous article.
1. + the load () method
When the learning application loads dyld, we locate the time to call the load_images method. The load method is commonly used in our daily development, and many interview questions will often appear, such as class, parent class, the order of calling the load method in the classification, etc. Here we first through the case clear load method call order, and then combined with the source code to analyze.
1. Case study
Introduce a case where the parent class Fater, the child class Son and their classification all implement the load method. See the following code:
@implementation Father + (void)load{NSLog(@"%s", __func__); @implementation Father (GF) + (void)load{NSLog(@"%s", __func__); } @end // subclass @implementation Son + (void)load{NSLog(@"%s", __func__); } @end @implementation Son (GF) + (void)load{NSLog(@"%s", __func__); } @endCopy the code
When you run the program, you will see that the system automatically calls the load method for each class. The order of invocation is: parent class -> child class -> classification. See below:
2. Process of the load method
So when was the load method called in the above example? As we know from the last article, calling sNotifyObjCInit also calls load_images, which can be implemented in the libobjc.a.dylib source code. See below:
In the implementation of the load_images function, find call_load_methods where the load method is called. We can also verify this from the comments. The +load method of class is called repeatedly in call_load_methods until there are no more.
- The load method
Call_class_loads is a function level load method in call_class_loads.
The source code gets the method from loadable_classes and calls it.
- Classification load method
In call_category_loads function level load method is implemented, as shown below:
The source code retrieves the method from loadable_categories and calls.
There’s a question to think about, and here we areThe load method
But where was this order determined?load_images
There’s a key part of it that hasn’t been studied yet!
3. Determine the order of calling the load method
Discover Load methods. The load method is processed here and placed in the corresponding loadable_classes and loadable_categories tables.
So what is the prepare_load_methods method doing? Go in and have a look. See the source code below:
- Non-lazily loaded class handling
What does the _getObjc2NonlazyClassList method do? Get all classes that implement the +load method (that is, non-lazily loaded classes) and add them to a static array loadable_classes. Schedule_class_load ();
static void schedule_class_load(Class cls) { if (! cls) return; ASSERT(cls->isRealized()); // _read_images should realize if (cls->data()->flags & RW_LOADED) return; // Ensure superclass-first ordering schedule_class_load(cls->superclass); add_class_to_loadable_list(cls); cls->setInfo(RW_LOADED); }Copy the code
First, determine whether the class is empty. If so, return directly. If the value is not null, check whether the value is RW_LOADED. If the value is RW_LOADED, the value is returned. We then recursively get the parent of the current class to ensure that the parent is processed first, and then call add_class_to_loadable_list to add the class and load methods to the loadable_classes array.
- Non-lazy load classification processing
What do _getObjc2NonlazyCategoryList method? Get all classes that implement + LOAD (non-lazily loaded classes), then determine if the class corresponding to the class is nil, skip if the class corresponding to the class is nil, initialize the class corresponding to the class instead, and add the class to a static array loadable_categories.
- Conclusion: Go
prepare_load_methods
, that is, after the preparation work is done, the procedure goes tocall_load_methods
, the callThe load method
.Call_load_methods method
We can see from the code that in this method the system will take the class itself+ load method
andThe +load method of classification
It’s all called, andClass + load
thanclassification
Is called first. So if you have multiple categories that are implemented+load
, which category to call first? It depends on compilation, who calls first at compile time.
So we understandThe load method
But that still leaves the question, how is the classification method loaded into the class? Later article again decrypt!
2. + the initialize () method
The initialize method is often asked during interviews, so what’s the difference between the initialize method and the Load method? Where is it called?
1. Initialize source analysis
In fact, we have found the entry point in the slow message lookup process. See below:
In this process, you first make sure that the class is implemented, that is, for lazily loaded classes (which do not implement the load method). The initialize method is called after the class is implemented. See below:
Only the invocation of the initialize method is covered here; the class implementation will be covered in a later section.
Continuing to trace the source code, I finally found the location of the method call: initializeAndLeaveLocked->initializeAndMaybeRelock->initializeNonMetaClass->callInitialize->objc_msgSend(cls, @selector(initialize))
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
Copy the code
- Through the source code can be found:
initialize
Is through theobj_msgSend
.Message is sent
The way the call is made, withThe load method
Calls are fundamentally different;The load method
Is called at the function level during the application loading phase, rather than being sent as a message.
So what’s the initialize call logic? The key is in the initializeNonMetaClass implementation, as shown below:
- Therefore, the following conclusion can be drawn: when a class is sending a message, the recursive method is adopted to determine whether the parent class is the first
isInitialized()
If there is none, the corresponding one will be calledThe initialize method
Because it is message sending logic, so if the subclass does not implementThe initialize method
, the parent class will be calledThe initialize method
.
2. Initialize case verification
The parent class Fater and the subclass Son implement both the load method and the initialize method. See the following code:
@implementation Father + (void)load{NSLog(@"%s", __func__); } + (void)initialize { NSLog(@"%s", __func__); @implementation Father (GF) + (void)load{NSLog(@"%s", __func__); } + (void)initialize { NSLog(@"%s", __func__); } @end // subclass @implementation Son + (void)load{NSLog(@"%s", __func__); } + (void)initialize { NSLog(@"%s", __func__); } @end @implementation Son (GF) + (void)load{NSLog(@"%s", __func__); } + (void)initialize { NSLog(@"%s", __func__); } @endCopy the code
If you start the program directly, you will find that only the load method is printed, and the initialize method is not triggered. This result also validates the previous analysis that the load method is automatically transferred during the application loading phase and the dyLD process. Initialize is triggered when a message is sent to the class.
-
Create the Son object and run the program. The result is as follows:
- The first call to the parent class
initialize
, and then call the subclassinitialize
- Because it’s implemented in the classification
initialize
Method, so the classification will be calledinitialize
load
Methods, limited toinitialize
The method call
- The first call to the parent class
-
Create a Father object and run the program. The result is as follows:
- It doesn’t trigger subclasses
initialize
methods
- It doesn’t trigger subclasses
-
Note: If neither the subclass nor the subclass class implements the initialize method, the parent class’s initialize method will be called twice when sending a message to the subclass! See below:
- When the subclass sends the message for the first time (if the parent has not been called), recursive processing is performed to ensure that the parent was called first
initialize
Method, and the subclass itself does not implement it, so the messaging process finds the parent class and calls it once! So it’s going to be called twice!
- When the subclass sends the message for the first time (if the parent has not been called), recursive processing is performed to ensure that the parent was called first