This is the fifth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

We talked about class loading in the previous article, but so far, we don’t know when the rWE of the class will be assigned, but we can guess that it’s in methodizeClass, but as we’ve been debugging, rWE is nil in this method, So are there other ways to make RWE worthwhile? We note that Attach Categories is attached to the methodizeClass method. Does category affect RWE?

The essence of the category

First, add a new category for the Person class:


@interface Person (A)
@property (nonatomic.copy) NSString *catName;
@property (nonatomic.assign) int catAge;
- (void)catInstanceA;
+ (void)catClassA;
@end

@implementation Person (A)
- (void)catInstanceA {
    NSLog(@"__%s__", __func__);
}

+ (void)catClassA {
    NSLog(@"__%s__", __func__);
}

@end
Copy the code

Then, the command line generates a CPP file for Person+ A.M. :

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+A.m -o Person+A.cpp
Copy the code

View the Person+ a.cpp file:

In the CPP file, we find that Person+A corresponds to A structure of type _category_t, which has the following structure:

Since the class has no metaclass, it contains instance_methods and class_methods, and the name should be the suffix A of the class, and CLS should be the Person corresponding to the class.

But when we look at Person+A, it does:

That’s because in the CPP static file, Person+A hasn’t been associated with Person yet, so here name just says Person, and CLS is 0;

We implement the NSObject protocol in Person+A.h, and then regenerate the CPP file to see its structure:

(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_A,

Let’s look at the list of methods:

You can see that there are class methods and object methods, but there are no setter and getter methods for properties. This means that it was determined at compile time that the properties in the category would not generate setters and getters.

So, how do the properties and methods inside Person+A load into the main class Person?

The category of the load

In the previous article, when debugging breakpoints:

Auto rwe = rw->ext(); I think I need to look at the ext() method of rw. If ext() has a value, then RWE has a value

Below the ext() method, we see the extAllocIfNeeded() method, which creates ext, so where is it called?

Full-text search shows several places to call extAllocIfNeeded:

  • attachCategories
  • objc_class::demangledName
  • class_setVersion
  • addMethods_finish
  • class_addProtocol
  • _class_addProperty
  • objc_duplicateClass

As we can see from the method names and comments, they are mostly runtime related, meaning that rWE is created at runtime, which is consistent with the WWDC2020 runtime optimization.

Since there is the Attach Categories operation in methodizeClass (judging by the annotation), we will focus on the attachCategories method next

attachCategories

Full-text search, where attachCategories is called finds the following:

  • attachToClass
  • load_categories_nolock

attachToClass

Next, we need to take a look at where attachToClass is being called. After searching, we find that only methodizeClass is being called:

Do you call the inside of if or the outside of yours? Previously is a method pass, so let’s see where methodizeClass was called, and what value was passed to previously?

Search for the previously found methodizeClass method, from the realizeClassWithoutSwift method, and all the realizeClassWithoutSwift methods in the source code, take nil;

So in other words, the attachToClass method that is finally called is:

The calling process is as follows:

realizeClassWithoutSwift–>methodizeClass–>attachToClass–>attachCategories

That is, during class loading, attachCategories is called;

load_categories_nolock

This process is analyzed later

The load method of the main class and classification is loaded separately

In the previous analysis, we have seen the loading process of the classification. Next, we have added the print information of intercepting Person on each process node. Next, let’s look at the execution process by case:

The main class is load, the class is Load

The execution process is as follows :readClass–> _READ_images –>realizeClassWithoutSwift–>methodizeClass–>attachToClass–> load_categories_NOLock –>at tachCategories

The main class doesn’t have load, the class has load

The execution process is readClass–>_read_images–>realizeClassWithoutSwift–>methodizeClass–>attachToClass

The main class has load, the class has no load

The execution process is readClass–>_read_images–>realizeClassWithoutSwift–>methodizeClass–>attachToClass

No load for the main class, no load for the class, nothing for the main function, okay

The execution process is as follows: There is no call after readClass

Load is the main class, but not all categories (4 categories)

There’s only one category without load

The execution process is readClass–>realizeClassWithoutSwift–>methodizeClass–>attachToClass–>attachCategories

There is no load for both categories

The execution process is readClass–>realizeClassWithoutSwift–>methodizeClass–>attachToClass–>attachCategories

There is no load for the three categories

The execution process is readClass–>realizeClassWithoutSwift–>methodizeClass–>attachToClass

Analysis of the single category loading process -attachLists

Next, let’s debug the case where both the main class and the category have loads:

In the realizeClassWithoutSwift method, look at the ro data and see that the classification method has not come in yet, so continue debugging down to attachCategories:

Execute down:

Mlist is a list of methods in the category, and its address is placed at the end of the mLists list.

PrepareMethodLists + ATTACH_BUFSIZ -mcount take the address and pass in the address of the last element:

Then go to the fixupMethodList method to sort:

Continuing, we enter the attachLists method, passing in a two-dimensional pointer (which eventually points to the method address of the class) :

Perform the following operations to enter attachLists:

Continue to perform:

We put the pointer to the main class method in the second position of the array, and the pointer to the classification method in the first position of the array; The classification method is first, the main class method is last;

Multiple classification load analysis -attachLists

Add multiple categories of the Person class that implement the load method:

We then run the project to debug because the load_categories_NOLock method applies to both the main class and the category. We debug the breakpoint here:

Here, the for loop happens to have four categories. When I = 0, intercepting Person+C, we have already gone through the previous process (single category loading). This time, let’s look at I = 1, loading the second category:

In the attachLists method we go to the first if condition:

Resolution:

OldCount = 2, addedCount = 1, newCount = 3

The old array has two elements: the first category and the main class. Now you need to add another category to generate a three-element array.

For the first loop, assign array()->lists[1] to the position of newArray->lists[1 + 1]; (of the main class)

In the second loop, assign array()->lists[0] to newArray->lists[0 + 1]; (First category)

So I’m going to put the two pieces of data that were in the old array, and I’m going to put them in the new array at 1 and 2

Continue to perform:

NewArray ->lists[0] newArray->lists[0] newArray->lists[0] newArray->lists[0] All the addresses in the array are Pointers;

Ro and RW data loading analysis:

The main class is load, the class is Load

Go to attachCategories, which has been analyzed before and will not be analyzed here

The main class doesn’t have load, the class has load

After the process goes to realizeClassWithoutSwift, print out the ro data:

BaseMethods contains 14 data, indicating that both the main class and the parent class methods are loaded, all from data().

The main class has load, the class has no load

BaseMethods contains 14 data, indicating that the methods of the main class and parent class are loaded, all from data().

No load for the main class, no load for the class

According to the stack information, it is found that the first message is sent through the process, and the data is stored in data().

The main class has load, but not all classes have LOAD

Person+A, Person+B, Person+C, Person+D; The process is the same as the main class and the classification to implement the load method, and finally the classification loading is implemented through attachCategories;

The load method is actually time consuming;