Hi 👋

  • Wechat: RyukieW
  • 📦 Archive of technical articles
  • 🐙 making
My personal project Minesweeper Elic Endless Ladder Dream of books
type The game financial
AppStore Elic Umemi

This article is based on objC4-818.2 source code

  • Series of articles:
    • [iOS app startup (I)] Dyld and main function
    • [iOS App Startup (2)] Environment configuration and Runtime initialization
    • [iOS app Startup (3)] Image file reading and loading
    • The process of realizing and initialize a Class is illustrated

preface

The last step in the process of realizing the realizeClass Class during the initialize and realize the realizeClass Class is to process the methodizeClass. This paper will take this as an entrance to explore the loading process of classification.

A, methodizeClass

Process method list, protocol list, attribute list, add categories.

See notes for important parts:

1.1 attachToClass Adds a category to a class

Continue to call attachCategories, and through a preliminary reading of the source code, it is found that this is the core step

Second, the attachCategories

Here is a translation of two important comments in the source code:

  • Add classified method lists, attributes, and protocols to the class
  • The first category to load is at the top
// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
Copy the code
  • Only a few classes have more than that at run time64A classification
  • I’m going to use a small volume hereThe stack
  • The categories must be added in the proper order, where they are read from front to back, local cache created from back to front, and calledattachLists. So the last order is correct.
/* * Only a few classes have more than 64 categories during launch. * This uses a little stack, and avoids malloc. * * Categories must be added in the proper order, which is back * to front. To do that with the chunking, we iterate cats_list * from front to back, build up the local buffers backwards, * and call attachLists on the chunks. attachLists prepends the * lists, so the final result is in the expected order. */
Copy the code

Main code:

static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{...constexpr uint32_t ATTACH_BUFSIZ = 64;
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;
    uint32_t propcount = 0;
    uint32_t protocount = 0;
    bool fromBundle = NO;
    bool isMeta = (flags & ATTACH_METACLASS);
    / / create rwe
    auto rwe = cls->data() - >extAllocIfNeeded(a);// Loop to add methods, properties, protocols
    for (uint32_t i = 0; i < cats_count; i++) {
        auto& entry = cats_list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta); .property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi); .protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta); . }if (mcount > 0) {
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
                           NO, fromBundle, __func__);
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) {
            flushCaches(cls, __func__, [](Class c){
                // constant caches have been dealt with in prepareMethodLists
                // if the class still is constant here, it's fine to keep
                return! c->cache.isConstantOptimizedCache(a); }); } } rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);

    rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
Copy the code

2.1 process

  • ExtAllocIfNeeded create rwe
  • Add methods, properties, and protocols in a loop

Third, the timing of classification loading

The core logic until the class adds categories is attachCategories. Let’s do a global search on attachCategories.

attachToClass

load_categories_nolock

There are two calls made above.

3.1 Test code preparation

Custom class

@interface RYCat : NSObject

@property (nonatomic.copy) NSString *nick;
@property (nonatomic.assign) NSInteger age;

- (void)yeah;
- (void)yeah2;
- (void)yeah3;

@end
Copy the code

classification

@interface RYCat (CateA)

- (void)funcA;
- (void)funcA2;
- (void)funcA3;

@end
Copy the code

The main function

RYCat *cat = [RYCat alloc];
[cat funcA];
Copy the code

3.1 Adding breakpoints and Logs

3.2 Scenario 1: Load of categories ✅ Load of main classes ✅ (Non-lazy loading)

The Log is as follows:

_read_images -- RYCat
realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
load_categories_nolock -- RYCat -- CateA
attachCategories -- RYCat -- CateA
attachCategories -- RYCat -- CateA
+[RYCat load]
+[RYCat(CateA) load]
-[RYCat(CateA) funcA]
Program ended with exit code: 0
Copy the code

Process:

  • _read_images
    • realizeClassWithoutSwift
      • attachToClass
        • load_categories_nolock
          • attachCategories

3.3 Scenario 2: Category Load✅ Primary Class No Load❌ (Non-lazy loading)

The Log is as follows:

_read_images -- RYCat
realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
+[RYCat(CateA) load]
-[RYCat(CateA) funcA]
Program ended with exit code: 0
Copy the code

Process:

  • _read_images
    • realizeClassWithoutSwift
      • attachToClass

Note that attachCategories are not called.

The lazy loading

The main class does not implement the +load method, but the class does, so the main class is not lazily loaded.

3.4 Scenario 3: Categories No Load❌ Main classes Load✅ (Non-lazy loading)

The Log is as follows:

_read_images -- RYCat
realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
+[RYCat load]
-[RYCat(CateA) funcA]
Program ended with exit code: 0
Copy the code

Process:

  • _read_images
    • realizeClassWithoutSwift
      • attachToClass

Note: Just as the categories have Load✅ and the main class has no Load❌, attachCategories are not called.

3.5 Scenario 4: Categories No Load❌ Primary Classes No Load Error ❌ (Lazy loading)

The Log is as follows:

realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
realizeClassWithoutSwift -- RYCat
attachToClass -- RYCat
Program ended with exit code: 0
Copy the code

Note: As with 3.3 and 3.4, attachCategories are not called.

Ponder: Why not callattachCategories

The compiler optimizes to merge classified data directly into the main class.

Four, more classification+loadExecution order

If there are multiple categories, what is the order of execution?

Create two categories as shown in the figure and implement the +load method respectively. So let’s think about the order of execution. Why is that?

4.1 the order

The output is as follows in order:

  • +[RYCat load]
  • +[RYCat(CateA) load]
  • +[RYCat(CateB) load]

Let’s look at the compile order first:

Change the compile order to see if it affects:

After adjusting the compilation order, the following output is displayed:

  • +[RYCat load]
  • +[RYCat(CateB) load]
  • +[RYCat(CateA) load]

4.2 the conclusion

In the case of multiple classes, the order in which +load is executed depends on the order in which it is compiled.

5. Execution sequence of multiple classification methods with the same name

If multiple categories are classified by the same name, in what order?

Implements the same name in both classesfuncA

In the multi-class +load execution order, we know that the order of compilation affects the performance of each class, and we also make a distinction here

5.1 CateA is compiled before CateB

  • +[RYCat load]
  • +[RYCat(CateA) load]
  • +[RYCat(CateB) load]
  • -[RYCat(CateB) funcA]

5.2 CateB is compiled before CateA

  • +[RYCat load]
  • +[RYCat(CateA) load]
  • +[RYCat(CateB) load]
  • -[RYCat(CateA) funcA]

5.3 Declare and implement funcA in the main class as well (CateB compiles before CateA)

  • +[RYCat load]
  • +[RYCat(CateA) load]
  • +[RYCat(CateB) load]
  • -[RYCat(CateA) funcA]

5.4 summarize

If multiple classes have methods of the same name, the methods of the first compiled class will be overwritten by the later compiled class. Methods with the same name in the main class are also overwritten.

Think and relate to objects

Impact on startup speed

When the Load method is implemented in 3.2 main class classification, it can be seen that the whole process will be complicated, so reducing the occurrence of such scenarios is conducive to improving the application startup speed.

We cannot mention categorization without mentioning associated objects

The assignment and release of associated objects will take you further.