preface
Following the read_images analysis of class loading in the previous part, the process of map_images → _read_images → readClass → addNamedClass → addClassTableEntry was explored in the previous part.
When loading realize non-lazy classes, first insert the class into the table addClassTableEntry, and then call realizeClassWithoutSwift to initialize the operation of the class.
realizeClassWithoutSwift
The main process includes: RW and RO assignment (placeholder) → parent and metaclass loading and association → setting member variables → setting marker bit → methodizeClass assignment (RW and ro real assignment).
Rw and ro assignments
// Get ro from the class
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
// Determine whether the RW has been allocated (future class)
if (ro->flags & RO_FUTURE) {
// Get ro from future classrw = cls->data(); ro = cls->data()->ro(); ASSERT(! isMeta); cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); }else {
// If it is a normal class
rw = objc::zalloc<class_rw_t>(); // Open up memory
rw->set_ro(ro); / / set the ro
rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // Set the tag
cls->setData(rw); // Set the rW of the class
}
Copy the code
Description:
- through
cls->data()
To obtainro
The data.rw
Open up space, willro
Assigned torw
thero()
In the. (Apple has optimized this accordingly. Please refer toWWDC- About runtime optimization)- Set up the
rw
theflags
, includingRW_REALIZED
(31
) saidrw
Whether the initialization is complete;RW_REALIZING
(19
) saidrw
Whether initialization is in progress;isMeta
(0 bit
) is a metaclass.- The assignment
rw
tocls
.
However, after actual debugging, the baseMethodList is empty after executing the process.
This is not a real assignment, but according to the class offset situation to occupy the corresponding position.
Parent and metaclass loading and association
The parent class
andThe metaclass
loading
// Load the parent and metaclass recursively
supercls = realizeClassWithoutSwift(remapClass(cls->getSuperclass()), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
Copy the code
Instantiate parent and metaclass recursively
ISA
Pure pointer processing
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
// the ISA of a metaclass ISA pure pointer
// Set the raw ISA to the cache
cls->setInstancesRequireRawIsa();
} else {
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
// OBJC_DISABLE_NONPOINTER_ISA is configured in the corresponding environment variable
if (DisableNonpointerIsa) {
Isa isa pure pointer if the environment variable is true.
instancesRequireRawIsa = true;
}
// Pure pointer for os_Object
else if(! hackedDispatch &&0 == strcmp(ro->getName(), "OS_object"))
{
hackedDispatch = true;
instancesRequireRawIsa = true;
}
// If the parent class is a pure pointer and the parent class exists.
else if (supercls && supercls->getSuperclass() &&
supercls->instancesRequireRawIsa())
{
instancesRequireRawIsa = true;
// rawIsaIsInherited indicates that inherited is a pure pointer
rawIsaIsInherited = true;
}
// If the pure pointer marks bit true
if (instancesRequireRawIsa) {
// Recursively sets the ISA of classes and subclasses to a pure pointer, where rawIsaIsInherited is the print flagcls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited); }}// SUPPORT_NONPOINTER_ISA
#endif
Copy the code
- judge
isa
Whether it is a pure pointer.- judge
os_object
,The parent class
Etc.Pure pointer
Is it necessary to recursively set pure Pointers?- A recursive set
class
andA subclass
theisa
Is a pure pointer.
The parent class
andThe metaclass
associated
// Associate the parent class with the metaclass.
cls->setSuperclass(supercls);
cls->initClassIsa(metacls);
Copy the code
Associate a parent class with a metaclass. Corresponding class inheritance chain with ISA go bitmap.
Sets member variables and flag bits
// Fix ivar offset, possibly recreate class_ro_t to update ivar
if(supercls && ! isMeta) reconcileInstanceVariables(cls, supercls, ro);// Set instance size (size of member variable)
cls->setInstanceSize(ro->instanceSize);
// Synchronize the flag bit
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if(! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) { cls->setHasCxxCtor(); }}Copy the code
- In the case of superclasses, and non-metaclasses are fixed
ivar
theoffset
- To reset
Member variables
The size of the.
methodizeClass
In the above rW and RO assignments, there is no method data (baseMethodList is empty) through the actual test, because the RW at this time is only an empty structure address, where SEL and IMP have not been associated.
static void methodizeClass(Class cls, Class previously)
{
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Load its own methods and properties into rWE.
method_list_t *list = ro->baseMethods();
if (list) {
// Prepare and fix the (sort) method list
prepareMethodLists(cls, &list, 1.YES, isBundleClass(cls), nullptr);
if (rwe) rwe->methods.attachLists(&list, 1);
}
// Load attributes to rwe
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
// Load the protocol to rWE
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
// If it is the root metaclass
if (cls->isRootMetaclass()) {
// Add the initialize method
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "".NO);
}
// Add the methods of the current class to unattachedCategories
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
}
Copy the code
- add
cls
Methods and properties torwe
In the.- Root metaclass judgment, add
initialize
Methods.- Add the loaded class to
unattachedCategories
In the table.
Rw and RO data validation
1. When the authentication is performed, it is found thatlist
Is empty.
The reason is that the ro at this time is actually the ro of the metaclass, and there is no method in the metaclass, so the metaclass needs to be filtered.
2. Verify again
Terminal View method
Note:
- If it is
For the first time,
loadingcls
There is no waycorrection
And here,Don't
Print out the method. This is because I’ve been debugging beforeThere are
Cache, so you can print out methods.- If there is no fix method, it will be executed
prepareMethodLists
Make method modification.
prepareMethodLists
static void prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle, const char *why)
{
// Core code
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
if(! mlist->isFixedUp()) {// Fix method (sort)
fixupMethodList(mlist, methodsFromBundle, true/*sort*/); }}}Copy the code
- Among them
addedCount
The value of1
.addedLists[0]
forro
thelist
.- If no correction is made, execute
fixupMethodList
Sort.
fixupMethodList
static void fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
// DyLD3 may have been marked unique, but not sorted
if(! mlist->isUniqued()) {// Iterate over the list of methods
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
// Get sel name and set.meth.setName(sel_registerNameNoLock(name, bundleCopy)); }}// select * from sel;
if (sort && !mlist->isSmallList() && mlist->entsize() == method_t::bigSize) {
method_t::SortBySELAddress sorter;
std::stable_sort(&mlist->begin()->big(), &mlist->end()->big(), sorter);
}
}
Copy the code
SEL is modified according to name, corresponding to binary search of slow message search.
Sorting validation
Before and after stable_sort above, add the following code to print the method before and after the sort.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name());
// Prints the method name
printf("Sorted: %s -- %p\n", name, meth.name());
}
Copy the code
The results are as follows:
Before Sorting: subInstanceMethod1 -- 0x100003A16 Before Sorting: subInstanceMethod2 -- 0x100003A29 Before Sorting: subInstanceMethod3 -- 0x100003A3c Before Sorting: SubInstanceMethod4 -- 0x100003A4F Before sorting: subInstanceMethod5 -- 0x100003A62 Before sorting: init -- 0x100003906 -------------- After sorting: SubInstanceMethod1 -- 0x100003A16 Sorted after: subInstanceMethod2 -- 0x100003A29 Sorted after: subInstanceMethod3 -- 0x100003a3c Sorted after: subInstanceMethod1 -- 0x100003A16 Sorted after: subInstanceMethod2 -- 0x100003A29 Sorted after: SubInstanceMethod4 -- 0x100003A4F After sorting: subInstanceMethod5 -- 0x100003a62 After sorting: init -- 0x7FFF7B905a35Copy the code
- The order before and after is the same, because it may have been corrected before, or it may have been added in the correct order.
init
The correct address was assigned.
Classification inquiry
Classification structure
Create the following categories and view the category structure.
@interface ZLSubObject (ZLExtend)
@property (nonatomic.strong.nullable) NSString *cate_name;
- (void)cate_instanceMethod;
+ (void)cate_classMethod;
@end
@implementation ZLSubObject (ZLExtend)
- (void)cate_instanceMethod {
NSLog(@"%s",__func__);
}
+ (void)cate_classMethod {
NSLog(@"%s",__func__);
}
@end
Copy the code
clang
To view
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
Copy the code
The source code
To view
struct category_t {
const char *name; / / class name
classref_t cls; / / class they belong to
WrappedPtr<method_list_t, PtrauthStrip> instanceMethods; // Attribute method list
WrappedPtr<method_list_t, PtrauthStrip> classMethods; // List of class methods
struct protocol_list_t *protocols; / / agreement
struct property_list_t *instanceProperties; // Attribute list
struct property_list_t* _classProperties; // Class attribute list (uncommon)
// List of metaclass methods
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
// List of metaclass attributes
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
// List of metaclass protocols
protocol_list_t *protocolsForMeta(bool isMeta) {
if (isMeta) return nullptr;
else returnprotocols; }};Copy the code
Rwe assignment process
When methodizeClass is loaded above, we get the value of rwe, but by printing, rwe is null. This is because RWE has a value if there is a classification (from WWDC). The assignment of rwe comes from the rw->ext() function. As follows:
class_rw_ext_t *ext(a) const {
// get_ro_or_rwe() gets the contents of rwe
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
Copy the code
Using the backward method, get_ro_or_rwe() is searched globally, and only extAllocIfNeeded() retrives rwe itself.
class_rw_ext_t *extAllocIfNeeded(a) {
/ / for rwe
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 {
/ / create rwe
return extAlloc(v.get<const class_ro_t*>(&ro_or_rw_ext)); }}Copy the code
This method determines whether an RWE exists, gets and returns it if it does, and creates an RWE if it doesn’t
Global search extAllocIfNeeded, only the attachCategories call fits best.
The invocation logic of attachCategories can be applied in attachToClass and load_categories_NOLock.
conclusion
So far, there are three paths for loading the classification:
realizeClassWithoutSwift
→methodizeClass
→attachToClass
→attachCategories
load_images
→loadAllCategories
→load_categories_nolock
→attachCategories
_read_images
→load_categories_nolock
→attachCategories
Note: In the third clause, the process prerequisite is executed only after load_images is executed. Only for categories that occur at startup, this is deferred until after the first load_images method of the _DYLD_OBJC_Notify_register is called.
* The specific process of classification loading will be explored later.