In the loading analysis of classes in the previous article, the loading process of non-lazy-loaded classes, the logic of RO, RW and RWE, and the sorting process of methods were analyzed. This article will focus on the loading process of lazy-loaded classes and classifications.

1. Introduce problems

First, a review of the method methodizeClass, also analyzed in the last article. See below:

  • The method will beroMethods, protocols, and attributes attached torweBut the tracing code will find out at this pointrweStill empty. SorweWhen is it created? That’s what we need to find out!
  • At the same time through global searchmethodizeClassMethod,previouslyAre passed innilThat’s understandableif(previously)The process in should be reserved for some future processing.

Go to the attachToClass method and find an entry point for classification processing: the attachCategories method. See below:

According to the analysis in the last article, unfortunately, the tracking shows that attachCategories is not called! Finally we found the place where the attachCategories method was called, the load_categories_NOLock method, by a backward derivation. However, from the analysis in the previous article, it seems that the load_categories_NOLock method also applies only to the category validity that implements the LOAD method. What about the classified loading process for lazy loads? What does attachCategories do?

  • To summarize, we are now addressing the following issues:
    • Load flow of classification?
    • What is the order of methods in different cases?
    • rweWhen is it created?
    • attachCategoriesWhat did you do?

Picking up where we left off in the last article, we look at the multicase classes and the loading process for the categories.

2. Load analysis of classification

Add a case, create a class LGPerson with two instance methods that implement +load(), and create a category LGPerson (LG) with an instance method that also implements +load(). See below:

// LGPerson @implementation LGPerson +(void)load{} - (void)saySomething{NSLog(@"%s",__func__); } - (void)sayHello{ NSLog(@"%s",__func__); } @end // classified@implementation LGPerson (LG) + (void)load{} - (void)sayHello_cate{NSLog(@"sayHello_cate %s", __func__); } @endCopy the code

1. Non-lazy loading classes and non-lazy loading classes

  1. This situation was analyzed in the previous article. No more analysis here, directly give the results, and verify!

    • Class initialization

      map_images->_read_images->realizeClassWithoutSwift->methodizeClass

    • Class initialization

      load_images->load_categories_nolock->attachCategories

  2. To verify, set breakpoints at key locations and run the program:

    LGPerson is filtered in methodizeClass method. At the same time, it is found that LGPerson started the class initialization process when calling map_images when dyld application is loaded. In the ro of the class, two instance methods of LGPerson have been successfully obtained, indicating that at this point the classification method has not been added to the class. See below:

    MethodizeClass will sort the methods in ro, and RWE hasn’t been created yet!

    The load_categories_NOLock method filters the LGPerson class and retrieves the corresponding class LG. As you can see from the stack information, the initialization process of the classification begins when load_images is called, also when the dyld application is loaded. See below:

2. Lazy loading classes and non-lazy loading classes

Now, before you run the program, think about it, the class is now lazily loaded, so what if the read_images process doesn’t go into realizeClassWithoutSwift? If the category is still non-lazily loaded, is the loading of the category still initialized by load_categories_NOLock?

As in the previous example, remove the +load method from the LGPerson class.

Set breakpoints at key locations and run the program:

The result was unexpected. During the application load phase, LGPerson was filtered in read_images and the realizeClassWithoutSwift method was called. See below:

Continue to run the program, obtain the ro data corresponding to LGPerson, and print the result of the method list as follows:

There are actually three methods, and the classified method has been placed in the corresponding data() of the class, at the top of the method list. Therefore, the following conclusions can be drawn:

  • Lazy loading classes and non-lazy loading classes

    Map_images ->_read_images->realizeClassWithoutSwift->methodizeClass and does not call attachCategories, the methods in the category are added to data() at compile time.

3. Non-lazy loading classes and lazy loading classes

Using the same example above, add the +load method from the LGPerson class and remove the +load method from the class.

Set breakpoints at key locations and run the program:

Similar to the second case (lazily loaded and non-lazily loaded classes), LGPerson is filtered in read_images and the realizeClassWithoutSwift method is called during application load. Obtain the ro data corresponding to LGPerson, and the result of the print method list is as follows:

There are also three methods, and the classification method has been placed in the data() corresponding to the class, and is at the front of the method list. Therefore, the following conclusions can be drawn:

  • Non-lazy loading classes and lazy loading classes

    Map_images ->_read_images->realizeClassWithoutSwift->methodizeClass and does not call attachCategories, the methods in the category are added to data() at compile time. If there are multiple categories, and the categories are lazy loading, the process is consistent!

4. Lazy loading classes and lazy loading classifications

Remove the +load method from classes and classifications. Also set the filter conditions in the key position, directly run the program, no filter to any content, run the end. When does this case load?

Looking back at the class load analysis from the previous article, we have concluded that lazily loaded classes are initialized the first time a message is sent. So in this case, is the classification the same?

Modify the code to send a message to the LGPerson class to see the result:

As in the case of lazily loaded classes, the data in the class is automatically added to data().

  • Lazy load classes and lazy load classes

    LookUpImpOrForward – > realizeClassMaybeSwiftMaybeRelock – > realizeClassWithoutSwift, won’t call attachCategories method, lazy loading, the first message is sent when the initialization, And the methods in the classification are automatically added to data().

5. Multiple classifications are supplemented

  1. Class is not lazy loading, there are multiple categories, all non-lazy loading

    Non-lazily loaded classes and non-lazily loaded classes, calling the attachCategories method to initialize the classes.

  2. Class is not lazy loading, there are multiple categories, all are lazy loading

    The methods in the classification are added to data() at compile time and the attachCategories method is not called.

  3. Classes are not lazily loaded, have multiple categories, and partially implement the +load method

    Non-lazily loaded classes and non-lazily loaded classes, calling the attachCategories method to initialize the classes.

  4. Lazy loading, there are multiple categories, all of which are lazy loading

    Lazy-loaded classes and lazy-loaded classes are initialized the first time a message is sent, and the methods in the class are automatically added to data().

  5. Lazy-like loading, with multiple categories, partially implements the +load method

    There are two cases:

    1. Only one category has been implementedloadMethods: Methods in the classification were added at compile timedata()Is not calledattachCategoriesMethods.
    2. More than one category has been implementedloadmethods

6. Lazy loading, more than one category implementedloadmethods

LGPerson class for lazy load, create three categories LG, LG2, LG3, where LG and LG2 implement the load method.

Map_images ->_read_images->realizeClassWithoutSwift Instead, the category process is filtered in load_categories_NOLock, as shown in the following picture:

And above is different, did not call attachCategories method, but the call of the else objc: : unattachedCategories. AddForClass (lc, CLS); CLS ->isRealized() is false, the class is not initialized! So what does addForClass do?

The simplest way to think about it is to associate a class with a category and place it in a BucketT. Complete the load_categories_NOLock loop three times and store the categories to the corresponding BucketT. See below:

Continue to run the program and filter LGPerson in methodizeClass.

Looking at the stack information, which is familiar, is the key process of the load method call, load_images->prepare_load_methods. Let’s review the prepare_load_methods method, as shown below:

Since the class is lazy loading, but the class is not lazy loading, and the class of LGPerson is not yet implemented, the realizeClassWithoutSwift method is called to initialize LGPerson.

In methodizeClass, the related methods, attributes, protocols, etc. of LGPerson class are processed. At this time, the data in the class has not been attached to this class. Continue running the program to call the attachToClass method, as shown below:

Get the BucketT corresponding to the class in attachToClass, get the list of its classes, and call the attachCategories method.

(3),attachCategoriesmethods

1. AttachCategories process analysis

Three arrays are created to handle methods, properties, and protocols, with a maximum storage space of 64, as shown below:

Initialize RWE:

    auto rwe = cls->data()->extAllocIfNeeded();
Copy the code

The methods, attributes, and protocols of the classification are processed, and relevant information is attached to the class. See below:

Get the corresponding method list, class method list if the current class is a metaclass, or instance method list otherwise, using the following code:

entry.cat->methodsForMeta(isMeta); Method_list_t *methodsForMeta(bool isMeta) {if (isMeta) return classMethods; else return instanceMethods; }Copy the code

The classification list is processed in a loop, and when data is inserted, it is inserted in reverse order. And mLists is a two-dimensional array. See below:

After finishing the sorting of methods, attributes and protocols, the related collection data is inserted into RWE, as shown in the following figure:

When dealing with a list of methods, we first sort the list of methods, but it’s important to note that mList is a two-dimensional array, and the sorting of methods is just sorting the methods within each category, not sorting all the methods into a set.

2. Rwe creates analysis

Rwe is initialized in attachCategories, as shown in the following code:

auto rwe = cls->data()->extAllocIfNeeded();
Copy the code

So let’s think about two things here,

  • When would it be rightrweCreate it?
  • inrweWhat did you do when you created it?
  1. First, search extAllocIfNeeded globally to see where the RWE creation process is called. A global search shows that RWE is initialized only when methods, categories, protocols, and versions are added to classes. For example, to add a protocol:

    In other words, rWE is created only when additional information needs to be added to the class in addition to ro data. This is consistent with our analysis in the class loading analysis.

  2. What was done internally when rWE was created? Traces the extAllocIfNeeded source code, which calls the extAlloc method for initialization. In the process, data of RO will be preferentially inserted into RWE, as shown in the following figure:

3. AttachLists analysis

Looking at the attachLists source code, addedLists is a pointer to a two-dimensional array. Different processing branches are set for different situations, as shown in the following figure:

  1. One dimensional array becomes two dimensional array

    The first time a class is entered, it initializes array() and sets the size of the array, adding the number of lists of the class to the number of lists of the original class. At the same time, put the class list in the last position, as shown in the following figure:

    Open the loop again and add the list corresponding to the category to array(), as shown in the following figure:

  2. A two-dimensional array becomes a two-dimensional array

    Array () has been initialized, so you will go to the branch shown in the following figure to verify that the current array() is in the same order as the first time you insert it, with the list of classes following and the list of categories preceding. See the following figure:

    In this process, malloc will open up a new newArray, re-initialize the size, insert the original array data into the newArray without changing the order, and insert the new classification list into the first position, as shown in the following figure:

  3. Creation of one-dimensional arrays

    When does a branch of a one-dimensional array enter? In fact, during the rWE creation process, we have already created the one-dimensional array. The rWE creation process has been analyzed above:

    • extAllocIfNeeded->extAlloc->attachLists.

    When attachLists is called, the ro data is placed first into the one-dimensional array corresponding to RWE, as shown in the following figure:

4. To summarize

  1. Through the above analysis, we can find that the initialization process of classification is still relatively complex, so in the usual development process try not to implement the load method of classification, in order to save performance.

  2. For method sorting, the methods of the class and category are not sorted together. During initialization, only the methods in the respective list are sorted.

  3. Rwe does not exist for every class, and rWE is initialized only when it is necessary to add methods, classes, protocols, and versions to a class.