Preface:
In the last article, we detailed the process of iOS Category. We mentioned that dyLD interprets the Mach -O file at compile time in the runtime library entry function _objc_init corresponding to three different time corresponding callback functions.
The load method is called when the dyLD load is finished, which is also when the load method is called. For those interested, read the last blog post.
Without further discussion, also open objC4 source.
To explore the process
1. Runtime library entry function
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
environ_init();
tls_init();
static_init();
lock_init();
exception_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
Copy the code
Map_images, load_images, and unmap_image are three functions that I mentioned in my last blog post, so I won’t go into details here. Just go load_images:
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if(! hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
mutex_locker_t lock2(runtimeLock);
/ * * * /
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
Copy the code
This code is divided into two steps, first prepare, then call. –> GO
2. Prepare the load method
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
Load the list of classes from the Macho file
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// Array: [< CLS,method>,< CLS,method>,< CLS,method>
schedule_class_load(remapClass(classlist[i]));
}
// For the classification of operations!
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if(! cls)continue; // category for ignored weak-linked classrealizeClass(cls); assert(cls->ISA()->isRealized()); add_category_to_loadable_list(cat); }}Copy the code
There are two more steps here
The load method of class 2.1 reads and sorts
// recursive call
static void schedule_class_load(Class cls)
{
if(! cls)return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// Ensure superclass-first ordering
schedule_class_load(cls->superclass);
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
Copy the code
In fact, it reads a list of all the classes from Mach-O
- Recursively according to the current class
superclass
The Pointers are arranged to satisfy a rule:
The load method of a class must be called after the load method of its parent.
- When the recursion
NSObject
And then find the parent class asnil
, out of recursion. End the permutation, and place eachload
methodscls
和method
Stored globallyloadable_class
In the structure.
2.2 Load method of classification reading and sorting
The general load process of a category is similar to that of a class. The list of categories is read from Mach-O and then iterated directly, adding the CLS and method of the load method of the category to the global loadable_categories structure.
That is, the order in which the load methods are loaded between different classes depends on the order in which the Mach-O file was compiled.
3. The load calls
call_load_methods
:
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
// Call the load method of the class
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
Copy the code
Here it’s a little bit easier. Start by going through the list of load methods of the previously stored class. Then iterate through the list of categories and call them in turn.
Conclusion:
load
Methods in theruntime
Library start runtime call.- Of a class
load
Method in all parent classesload
After the method call.- Classification of
load
Method in the classload
Method.- Between different categories
load
The method call order is related to the compile order.