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.

  1. 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.

  1. 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 methodBut where was this order determined?load_imagesThere’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:

  1. 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.

  1. 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: Goprepare_load_methods, that is, after the preparation work is done, the procedure goes tocall_load_methods, the callThe load method.Call_load_methods methodWe can see from the code that in this method the system will take the class itself+ load methodandThe +load method of classificationIt’s all called, andClass + loadthanclassificationIs 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 methodBut 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:initializeIs through theobj_msgSend.Message is sentThe way the call is made, withThe load methodCalls are fundamentally different;The load methodIs 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 firstisInitialized()If there is none, the corresponding one will be calledThe initialize methodBecause 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.

  1. Create the Son object and run the program. The result is as follows:

    • The first call to the parent classinitialize, and then call the subclassinitialize
    • Because it’s implemented in the classificationinitializeMethod, so the classification will be calledinitialize
    • loadMethods, limited toinitializeThe method call
  2. Create a Father object and run the program. The result is as follows:

    • It doesn’t trigger subclassesinitializemethods
  3. 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 firstinitializeMethod, 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!