The introduction of
In class loading, we know that class loading is implemented in the realizeClassWithoutSwift function, but we also have some ❓,
-
❓auto ro = (const class_ro_t *) CLS ->data(); Where does the data() come from?
-
❓ we know we have ro, rW, rWE, why does RW copy from ro, why does RWE exist, and when did rWE get assigned?
-
❓ What is the nature of classification, what is the loading process for classification, and why should you avoid overusing the load method
Ro, RW, rWE
Auto ro = (const class_ro_t *) CLS ->data(); Ro is from CLS ->data(), print it out
CLS ->data();
class_rw_t *data() const {
return bits.data();
}
/ / click bits. The data ()
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
Copy the code
CLS ->data() reads data from Macho and returns a pointer type. If the received data type corresponds to the data structure in the previous type, then it will be assigned. Auto ro = (const class_ro_t *) CLS ->data(); Put a break point at the end to print out the details.
- ro
Ro is clean memory. It is read from disk. It is read-only and does not change the space of the contents after loading
- rw
Rw is dirty Memory. Rw is a run-time structure that can be read and written. It can add properties, methods, and so on to classes. Memory that changes at run time.
- rwe
Rwe is the equivalent of extra information for a class, because very few classes actually change their content during actual use. So to avoid resource consumption, there is the RWE runtime. If you need to dynamically add methods, protocols, etc., to a class, you create an RWE and attache the ro data to the RWE first. Rwe data is preferentially returned when read, or ro data is returned if RWE is not initialized. Rw contains ro and RWE, where RW is dirty memory and ro is clean memory. To make the dirty memory take up less space, the variable parts of the RW are extracted as RWE; The more clean memory, the better, and the less dirty memory, the better. Because of the underlying virtual memory mechanism of iOS, when the memory is insufficient, part of the memory will be recycled. When the memory needs to be used again, it will be loaded from the hard disk, namely swap mechanism. Clean Memory is memory that can be reloaded from the hard disk. The macho file dynamic library in iOS belongs to this type. The dirty memory is the data generated during the running. The dirty memory cannot be reloaded from the hard disk, so it must always occupy the memory. When the physical memory of the system is tight, the clean memory memory is reclaimed. So the more clean memory the better, the less dirty memory the better; Apple makes such a detailed division of RO, RW and RWE in order to better distinguish between dirty memory and clean memory.
So when does rWE come in? We know from the rWE explanation above that when we add a category, there will be RWE. So how does the category data load?
Nature of classification
Example code:
//main
@interface Person (Stu)
+(void)sayHappy;
@end
@implementation Person (Stu)
+(void)sayHappy {
NSLog(@"hahahhaha");
}
@end
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
appDelegateClassName = NSStringFromClass([AppDelegate class]);
[Person sayHappy];
}
return 0;
}
@end
Copy the code
We write a category of Person Person+ stu.h, and then we call it. usexcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
To generate the main CPPWe look at the_category_t
structureThere are name, CLS, instance_methods, class_methods, Protocols, properties. We say that the internal implementation of the class does not distinguish between the classification method and the instance method, why is it listed in the classification, in fact, the classification has no metaclasses, so it is listed in this place. We add two properties to it:
@property (nonatomic, copy) NSString *stuName;
@property (nonatomic, copy) NSString *gender;
Copy the code
When looking at the main. CPP
We see that there are no corresponding implementations of the setter and getter methods. There is also no member variable with _, so you generally do not add attributes to a class. If you do, you cannot retrieve them. You can use runtime associated objects to add attributes to a class.
If (rwe) rwe->methods. AttachLists (&list, 1); So when is RWE going to assign? Let’s see what its data structure is
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
} else {
return extAlloc(v.get<**const** class_ro_t *>(&ro_or_rw_ext)); }}Copy the code
Created around extAllocIfNeeded, global search to see where it’s useful, We see that at some attachCategories, DemangledName, class_setVersion, addMethods_finish, class_addProtocol, _class_addProperty, and objc_duplicateClass are used in dynamic processing functions. The attachCategories method speculates that it relates to a category. Let’s look at where the calls are. We can see that they apply to attachToClass and load_categories_NOLock. So let’s verify that by creating a category and calling some sample code in the main function
//LGPerson.h
@interface LGPerson: NSObject
@property (nonatomic, copy) NSString *name;
- (void)saySomething;
@end
//LGPerson.m
@implementation LGPerson
+ (void)load{}
- (void)saySomething{
NSLog(@"%s", __func__ );
}
//LGPerson+LGA.h
@interface LGPerson (LGA)
@property (nonatomic, copy) NSString *cateA_name;
@property (nonatomic, assign) int *cateA_age;
- (void)saySomething;
- (void)cateA_instanceMethod1;
- (void)cateA_instanceMethod2;
+ (void)cateA_classMethod1;
+ (void)cateA_classMethod2;
@end
//LGPerson+LGA.m
@implementation LGPerson (LGA)
+ (void)load{}
- (void)saySomething{
NSLog(@"%s", __func__ );
}
- (void)cateA_instanceMethod1{
NSLog(@"%s", __func__ );
}
- (void)cateA_instanceMethod2{
NSLog(@"%s", __func__ );
}
+ (void)cateA_classMethod1{
NSLog(@"%s", __func__ );
}
+ (void)cateA_classMethod2{
NSLog(@"%s", __func__);
}
@end
//main
LGPerson * person = [LGPerson alloc];
[person saySomething];
[LGPerson cateA_classMethod1];
Copy the code
There’s a load method in a class, and there’s a load method in a class
Let’s explore how this situation loads using methods as an example. Put a breakpoint on the class’s implementation realizeClassWithoutSwift
Select * from ‘class’ where ‘class’ = ‘class’; select * from ‘class’ where ‘class’ = ‘class’;_read_images Non-lazily loaded class
->realizeClassWithoutSwift
->methodizeClass
->attachToClass
->load_categories_nolock
->attachCategories
->attachLists
load_categories_nolock
static void load_categories_nolock(header_info *hi) {
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
size_t count;
auto processCatlist = [&](category_t * const *catlist) {
for (unsigned i = 0; i < count; i++) { category_t *cat = catlist[i]; Class cls = remapClass(cat->cls); locstamped_category_t lc{cat, hi}; .if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
if (cls->isRealized()) {
// If the instance is a non-lazily loaded class, go here
attachCategories(cls, &lc, 1, ATTACH_EXISTING);
} else {
objc::unattachedCategories.addForClass(lc, cls); }}... } processCatlist(hi->catlist(&count)); processCatlist(hi->catlist2(&count));Copy the code
Auto processCatlist = [&](category_t * const * catList) {} is a block made by processCatlist(hi-> catList (&count)); Place passed into
You can seecategory_t
In the c++ analysis, the name is not determined, so the Person is displayed, indicating that the value of the category is assigned at run time.
attachCategories
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; .for (uint32_t i = 0; i < cats_count; i++) {
auto& entry = cats_list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
if (mcount == ATTACH_BUFSIZ) {//ATTACH_BUFSIZ = 64
// Method sort
prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__ );
rwe->methods.attachLists(mlists, mcount);
mcount = 0;
}
/ / go heremlists[ATTACH_BUFSIZ - ++mcount] = mlist; fromBundle |= entry.hi->isBundle(); }...if (mcount > 0) { prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle, __func__ ); rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount); . }... }Copy the code
mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
Let’s print out mlist and mLists ATTACH_BUFSIZ – ++ McOunt = 63
Put the address of the class’s address method list on the last element.
mlists + ATTACH_BUFSIZ - mcount
Print out its value and see that it’s a second-level pointer,(method_list_t **) $1 = 0x00007ffeefbf5ae8
attachLists
Let’s look at attachLists
void attachLists(List* **const** * addedLists, uint32_t addedCount) {
if (addedCount == 0) * *return* *;if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
newArray->count = newCount;
array()->count = newCount;
for (int i = oldCount - 1; i >= 0; i--)
newArray->lists[i + addedCount] = array()->lists[i];
for (unsigned i = 0; i < addedCount; i++)
newArray->lists[i] = addedLists[i];
free(array());
setArray(newArray);
validate();
}
else if(! list && addedCount ==1) {
// 0 lists -> 1 list
list = addedLists[0];
validate();
}
else {
// 1 list -> many lists
Ptr<List> oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
/ / set the array () - > count
array()->count = newCount;
// Put oldlist after array()->lists
if (oldList) array()->lists[addedCount] = oldList;
for (unsigned i = 0; i < addedCount; i++)
// Assign a value to the newly added listarray()->lists[i] = addedLists[i]; validate(); }}Copy the code
The first time this class is loaded, our method will go below // 1 list -> many lists inarray()->lists[i] = addedLists[i];
Print a breakpoint, print it outaddedLists[i]
The breakpoint moves backwards to print the values in array->list[]We see that array->list[I] is all PTR, and PTR is a pointer to method_list_t. In this section of code, we see that the array has length 2, and we put a pointer to method_list_t, so we’re going to write an extra category, and we’re going to add the following
//#import "LGPerson+LGB.h"
@interface LGPerson (LGB)
- (void)sayNothing;
@end
//#import "LGPerson+LGB.m"
@implementation LGPerson (LGB)
- (void)sayNothing {
NSLog(@"sdfsf");
}
@end
//main
[person sayNothing];
Copy the code
Print newArray->lists[I]; print newArray->lists[I]; print newArray->lists[I];
Then, in the first place, insert the last compiled category, as shown in the figure below where we print LGBFree (array()); // 1 list -> many lists
There’s no load method in a class, there’s a load method in a class
Call the _read_images non-lazy Load class ->realizeClassWithoutSwift->methodizeClass->attachToClass When did the classification method load? We put a breakpoint on realizeClassWithoutSwift and print the ro information (for simplicity, we removed LGB from the code at this time). As shown in the following print, we already have the classification information at this time. In this case, the classified information is obtained in data().
There’s a load method in the class, there’s no load method in the class
Use the following method to call _read_images non-lazy-loading class ->realizeClassWithoutSwift->methodizeClass->attachToClass to verify that the class method is also obtained in data().
There’s no load method in the class, there’s no load method in the class
Lazy loading class
The method for loading classes and classifications on the first message forward is also obtained from data().
If you have multiple categories, some of them have a load method and some of them don’t
The main class load method is not implemented
_read_images Non-lazily loaded class
->realizeClassWithoutSwift
->methodizeClass
->attachToClass
The classification method is obtained from data()
The main class has a load method
_read_images Non-lazy Loading class ->realizeClassWithoutSwift-> load_categories_NOLock ->attachCategories A category method is dynamically added from attachCategories
Above all: Try to avoid using too many load methods