preface

Load_image load_image load_image unmap_image Load_image Load_image Load_image Load_image Load_image Unmap_image There are some methods we didn’t cover in the previous article, but this article continues with class loading.

Loading of lazy and non-lazy classes

realizeClassWithoutSwift

We mentioned that if it’s a non-lazily loaded class, it calls realizeClassWithoutSwift, so let’s explore realizeClassWithoutSwift. Take a look at the entire method, where 2544-2554 lines of code were added to explore the helper methods written by the Person class.

We said that realizeClassWithoutSwift is the only class that is not lazily loading, so we create the Person class, add the +load method and we’re going to prepare the code.

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
- (void)instanceMethod1;
- (void)instanceMethod2;
- (void)instanceMethod3;
+ (void)sayClassMethod;
@end

@implementation Person
+ (void)load{
    
}
- (void)instanceMethod3{
    NSLog(@"%s",__func__);
}
- (void)instanceMethod1{
    NSLog(@"%s",__func__);
}
- (void)instanceMethod2{
    NSLog(@"%s",__func__);
}
+ (void)sayClassMethod{
    NSLog(@"%s",__func__);
}
@end
Copy the code

We run the code at the break point of line 2551The CLS passed in by this call to realizeClassWithoutSwift is the Person class.Sometimes it's better to look at the classes we've created, and adding some helper methods to quickly find the classes we need can save a lot of time. This is the idea behind the study of the source code. The break point goes down,Line 2556 If (CLS ->isRealized()) return CLS; If the class has already been classloaded, it returns directly

We operate on ro and RW

Let’s look at lines 251-2575So let’s talk about what this judgment does let’s look at line 2561, the method is to read the data() of the current CLS let’s move the breakpoint to line 2562, what does ro contain

We see that ro has a class name, a list of methods, 8, and the first method is called instanceMethod3.

The above method is that we read data from the assembled macho file and convert it according to a certain input data format (strong conversion to class_RO_T * type). At this time, ro has no relation with our CLS. The judgment, further down line 2563, is to determine whether the current CLS is a metaclass. This is not a metaclass, so it goes down

Line 2571 is applying and opening the zalloc, which contains THE RW. At this time, the RW is empty. Let’s see what the RW hasWe see the values are emptyWhere ro_or_rw_ext is ro or rw_ext, ro is clean memory and rw_ext is dirty memory.

Line 2572 sets the RW we created to our RO, and line 2573 copies the class’s data back to the RW. Let’s verify that

At this point the breakpoint is line 2572, at which point we print CLSAt this point we find that the last address is empty, and when we move the breakpoint to line 2578, we are printingThe last address was also empty. The CLS data is copied again, so why is it empty?

This is because ro is a clean memory address, so why is there a clean memory address and a dirty memory address? This is because the iOS running will result in constant to add and delete memory, will be more serious to the operation of the memory, in order to prevent modification of the original data, so the original clean memory copy a to the rw, why rwe have rw (memory) dirty, this is because not all of the classes for dynamic insertion, deletion. When we add a property, a method changes a lot of memory and has a big impact on memory consumption, so when we dynamically process the class, we generate an RWE.

Let’s look at the reading of ro:We saw above that ro can be read in two cases, class_rw_ext_t and class_rw_ext_t.

Let’s move on to the important method, as shown below:I’m going to call the parent class and the metaclass and get them to do the same thing, and that’s why I’m going to do the parent class and the metaclass hereDetermine the inheritance chain relationship, there will be recursion, when the CLS does not exist, return

Further down, at line 2604, isMeta is YES because it is indeed a metaclass. cls->setInstancesRequireRawIsa(); The method is to set ISA at line 2642 to finish the inheritance chain, continuing down to line 2649We’re going to change the CLS to be an address instead of a Person. Why is that? That’s because the topmetacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil); Method takes the metaclass.So let’s verify that.We see that the CLS is indeed metaclass at this point. Keep going:The method shown in the figure below is that the system sets the Cxx method for us. Moving on, we come to another important methodMethodizeClass (). MethodizeClass ()

MethodizeClass method

Let’s look at the methodizeClass methodI’ve also written auxiliary code to quickly find the class we want, aboveWe know that realizeClassWithoutSwift is constantly recursing and re-copying data() (equal to ro), but we don't see rw, rwe processingLet’s see if this method does something to rw, rwe

We know that there is a methodList in RO, and we use binary search when we do method search, and sort SEL in the middle

Let’s look at the method list order firstLeave it there. Let’s keep going At this point the list is there, so go into judgment and prepareMethodLists. PrepareMethodLists sort the methods

Let’s look at the prepareMethodLists methodWe also added a breakpoint at line 1239 to quickly find the Person class,The reason I made this judgment on line 1238 is to prevent metaclasses from having an effect.

As the code goes down, it comes back downSo this is going to go to fixupMethodListWe’re going to go to line 1215, and at line 1204-1212 we’ve processed sel name, sort is true, so now we’re going to go to line 1216, and line 1216-1217 is just sorting (by SELAddress), method doesn’t matter, we’re going to run to the end, Let’s reprint the method

This is not in the same order as the methods we printed above, so prepareMethodLists serializes the methods. When we talked about method finding, we used dichotomies to find things

Back to the methodizeClass methodWe see that the Rwe is NULL, which means that rew is not assigned and does not go. Why is that?

So let’s just put that out there for a second, in a non-lazy load we know when realizeClassWithoutSwift is called, so when does a non-lazy load call realizeClassWithoutSwift, we’ll say in our main function, Also remove the +load method and run the code:In the realizeClassWithoutSwift method, the break point comes and we type the stack message as follows

We know from above that when you send a message to Person for the first time, it’s going to go realizeClassWithoutSwift. Because a class has a lot of code, a lot of sorting and temporary variables, it would take a long time to load them all in front of main. If the class is never called, it doesn’t need to be loaded in advance. So lazy loading improves performance.

This part of the code is actually shown when the message is sent

When a class alloc, do a method lookup, and if the class isn’t loaded, load it. This means that the class has already been loaded before the class object is created and the method is called.

Here’s a diagram of lazy loading and non-lazy loading

Supplementary: Category (category)

We write the following code in the main.m function

@interface Person (C)

@property (nonatomic, copy) NSString *cate_name;
@property (nonatomic, assign) int cate_age;

- (void)cate_instanceMethod1;
- (void)cate_instanceMethod3;
- (void)cate_instanceMethod2;
+ (void)cate_sayClassMethod;

@end

@implementation Person (C)

- (void)cate_instanceMethod1{
    NSLog(@"%s",__func__);
}

- (void)cate_instanceMethod3{
    NSLog(@"%s",__func__);
}

- (void)cate_instanceMethod2{
    NSLog(@"%s",__func__);
}

+ (void)cate_sayClassMethod{
    NSLog(@"%s",__func__);
}
@end
Copy the code

And then using clang to generate.cpp files, let’s see what sort of.cpp looks like when we open the main. CPP file, we see something like this_CATEGORY_Person_ = _category_t = _category_t = _category_tWe find that _category_t is a structure with names, CLS, object method list, class method list, protocols, attributesThe reason categories have two lists is because they do not have meta-categories, and the sorting methods are inserted into the class at run time via attachToClass

This is the same structure as our category_t modifier, where instance_methods is assigned _CATEGORY_INSTANCE_METHODS_Person_. Let’s search globallyWe see that this is an object method, there are three of them, and we see that we have a method name, signature, address, and this is the same as the method_t structure.

But we found that our property does not have set and get methods in.cpp, so let’s look at the property assignment _PROP_LIST_Person_ and search for it We found that there were attributes but no set and GET methods, so there were no set and GET attributes in the classification, and we needed to use runTime for attribute association.

We found that the category is essentially a category_T form and now let’s look at how the category is loaded into memory, okay

Classification loading

Now let’s create a classification, and write down the following method for classification

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
- (void)instanceMethod1;
- (void)instanceMethod2;
- (void)instanceMethod3;
+ (void)sayClassMethod;
@end

@interface Person (A)
- (void)instanceMethod1;
- (void)cateA_1;
- (void)cateA_2;
- (void)cateA_3;
@end

@interface Person (B)
- (void)instanceMethod1;
- (void)cateB_1;
- (void)cateB_2;
- (void)cateB_3;
@end
Copy the code

MethodizeClass = methodizeClass = methodizeClass = methodizeClass = methodizeClass = methodizeClass (Because we know above that if the class is loaded, it must call the realizeClassWithoutSwift method, which in turn calls methodizeClass) We break at line 1468 and see that we go to line 1480, where the CLS is Person, and we see the method unattachedCategories, which is initialized in runtime_init. Let’s look at the attachToClass method implementationWhen we run it, we find that the code does not go into line 1150-1161. Why is this? The reason is the attachCategories method in line 1154. Let’s analyze it in detail

AttachCategories method

Let’s start with a method overview

Looking at the whole method, we need to know where the research focus is. We need to study how the classification method is loaded. Look at line 1400. So let’s start with that.

So let’s break it off at the point where we write that the current class is the Person class, and let the code run in, be decisiveNow we find cats_count, we have two categories, why is this one? The reason is that the attachCategories are looped in, with only one at a time, so let’s look at the MList.We can see that the mlist already has methods. The first one is the Person(B) method, and there are four methods. Let’s print entry

Remember that when we were updating the classification, we saw in the.cpp file that the name of the classification was Person, and the name of the classification was B, which means that the default assignment was Person at compile time, and the name of the classification was B at run time.

So let’s go down to line 1369, where the McOunt is not equal to ATTACH_BUFSIZ, so let’s go back to line 1374 and process the mlist, and let’s see what happens.

There are a total of 64 positions, with [ATTACH_BUFSIZ – ++ McOunt] where ATTACH_BUFSIZ is 64, the method will also subtract McOunt from 64 by a constant +1, resulting in positions equal to the list, which is reverse insertion. We see that bit 63 is 0x0000000100003360, and when we get entry. Cat, its instanceMethods = 0x0000000100003360. Reverse insertion was also demonstrated.

We see that mlist is method_list_t, which is a one-dimensional array, and we store mlist to Mlists, so mlists is a two-dimensional array

ExtAllocIfNeeded implementation

I’m going to go further and get to 1400 linesWe see that rwe has a value. We said that rwe has no value at all. When did it get assigned? Let’s look at line 1348 of the methodClick on the method extAllocIfNeeded to see the implementationWhy initialize RWE at this point?Because we're going to add methods and protocols to this class, we're going to have to deal with clean Memory. So when will the RWE be initialized? We searched for extAllocIfNeeded and found that there are several cases in which extAllocIfNeeded will be called to initialize rWE.AddMethod 3. Class_addProtocol 4._class_addProperty(Not limited to these types)

Let’s look at the extAlloc methodAt the breakpoint, we print rWE

At line 1283, the initialization is done, and nothing is printed because rwe is only initialized, not assigned.

If WE keep going, we’ll get to line 1294, we’ll get the List, we’ll print the ListWe see that the list at this point is a list of its own class, and further down we come to attachLists.

AttachLists method is implemented

If we look at the attachLists method,PrepareMethodLists 1399 lines of code, which talks about sorting methods above. Let’s look at the attachLists method implementationThe first is line 906. The addedCount passed in is 1 and list does not exist. Then list is equal to the first element of addedListsThe above isPut the newly inserted value at the front of lists and the old value at the back. This is because the value of the newly inserted value is greater than the old one, similar to the LRU algorithm of YYCache.And that tells you somethingIf a class has the same method as this class, the class method is called first.This is the verification result.

Let’s move on to the last case where we have multiple lists inside of multiple listsThis is consistent with the situation that oldList has only one,They put the new ones at the top of the table. It’s a little bit more intuitive to draw it up here

So let’s verify that

Validation process

So the Person up here came in, and I’m going to go to line 908, and I’m going to print it here We find that only addedLists have only one method, but why p addedLists[1] has a value, the reason is that the pointer is continuous and its value is the address, but there may be nothing in it. P addedLists[0] take a pointer to the instanceMethod1 method, so this is a one-dimensional assignment.Let’s keep goingWe see that the list is method_list_t. Let’s proceed decisively, the method is finished, and the original method is completed.That is, the methods of this class are loaded when the initial RWE is created, and then the class loading begins

Go back to the attachCategories method, go to the mList, let’s print it outThis is our classification method, and when we go through a bunch of processes, it comes back toAgain we go to the attachLists method, and before we go to attachLists, we print something We see attachLists passing mlists + attach_bufsiz-mcount is the last address of mlists.

So we’re going to get the else methodCode to run with last, printWe see that list_array_TT contains method_t, and method_t contains method_list_t, as shown in the figure below

List_array_tt is a two-dimensional array that contains a lot of method_T, and method_T is a one-dimensional array that contains method_list_t.

If we go to the top when we add another category, we put a breakpoint, go to the Person class again, go to attachLists, and then go to the topSo let’s print it outinstructionsThe first address of array() stores the instanceMethod1 method of class B. When this method completes, we are doing the same printAt this point we add the classification A method to array(). So the attachLists method adds the method to the top of array(). Verify what we said above.

From the above explanation, we understandAttachCategories means to prepare the category data and insert it into the class method list.Above,The lines we studied: map_images->map_images_nolock->_read_images->realizeClassWithoutSwift->methodizeClass->attachToClass->attachCategories-> AttachLists (attach method to class()). Now let’s see if there’s another place to call attachCategories, so let’s do a global search We see that red box 1 is attachToClass and red box 2 is the load_categories_NOLock method.So does this approach work? We print logs and run projects in the roomWe find that this method is executed, so let’s examine the execution line of this method. Let’s keep looking upAnother line is found: load_image->loadAllCategories-> load_categories_NOLock ->attachCategories.

Load_image load_image load_image load_image load_image load_image load_image load_image load_image load_image load_image The exploration above is that we implemented the +load method for both classes and categories. So let’s look at what happens to the class, with or without the +load method. To load the attachCategories method, we just need to look at the attachCategories method.

1. One of the classes has a +load method, and the class has a +load method

We see a category that only loads the implementation +load, but does not load the implementation

2. There is a +load method for all categories and a +load method for classes

Load implements the +load classification, does not implement the +load method

3. The class does not implement the +load method, there is a +load methodWe found that when realizeClassWithoutSwift was printed, methods (including categorization) were added and not sorted. Continue down to methodizeClass and find that the method with the same name is sorted, while the method without the same name is not sorted. We’re going to print the sort method prepareMethodLists break pointWe see that addedLists are a one-dimensional array, and we’ll call fixupMethodList later to get to this methodLine 1216 is sorted by the address of the method nameAt this point we seeMethods with the same name are sorted, and methods without the same name are sorted through IMP from smallest to largest. Ditto aboveNote fixupMethodList handles namesames first, and then imPs from small to large, but this is the same class of methods, different classes of methods do not handle this method.

If the main class is implemented and the classification is not implemented, then the classification method is taken from data() and only handles methods with the same name.

4. The main class does not implement the +load method, nor does the class implement the +load methodWhen we find readClass and call realizeClassWithoutSwift, we’re loading the method during a method call, and we’re at the readClass interrupt point

There are 16 methods in ro, indicating that there are already classification and class methods. Methods are also obtained from data().

5. The main class does not implement the +load method, the classification implementation +load method, run the code:We found that the attachCategories method was used, but we found that the _getObjc2NonlazyClassList method was not used. In readClass we found that count was 8Let’s print it out and see what are these 8 methodsweThese 8 methods are Person methods without categories_nolock. The categories_categories_nolock is applied in the "load_categories_nolock" category.

We found that when the classification is implemented and the main class is not implemented, it forces the main class to become a non-lazy loading class, loaded ahead of time.

The last

The above article and this article are to say the class of loading, content and more, this is a supplement to the previous article. Write to 2 o ‘clock in the morning, finally write almost (expand content to continue to write tomorrow). And I’ve drawn a picture at the end to sort of illustrate the whole process

expand

We broke the point while readClass, print the CLS and find bits 0x00000000.But we find 2561 lines of code calling the data() method again,The system call data() is equivalent to null.data(). So let's verify that bit has any value at all.You can see it up hereThe last address printed by the X /4gx CLS is 0x0000 because the memory of the CLS is incomplete. It also suggests thatWhen the memory is not perfect, it can be identified by the address pointer.

When does a bit in memory have a value? Let’s keep going downLet’s look at the instancesRequireRawIsa condition, which is necessary to initialize an ISAGo back to the realizeClassWithoutSwift method and keep goingWhen the setInstanceSize value changes, we move onWe find that the value changes again after the setHasCxxDtor method is executed