十二、+ load方法分析
2020-09-24 本文已影响0人
KB_MORE
load方法加载顺序
图片.png类: LGPerson
子类: LGTeacher
分类1: LGPerson+EatFirst.h 先创建
分类2:LGPerson+DrinkSecond 后创建
调用顺序
类
->子类
->分类
(后创建) ->分类
(先创建)图片.png
注意
: 这里分类的load
调用顺序是和编译顺序有关的(Compile Sources
在前面,先调用那个)和创建顺序无关, 不过我们一般不会刻意的去调整文件在Compile Sources
的顺序
探究
首先我们先在类中重写下load方法, 然后打个断点, 运行项目,可以看到堆栈的调用情况
可以看到在
load
调用之前调用了load_images
, 我们在objc
的源码中可以找到load_images
的实现,其实
load_images
是在_objc_init
函数中调用的, 下面看下几个函数的实现
objc
的源码可以查看源码调试准备
_objc_init
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();
runtime_init();
exception_init();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
load_images
void
load_images(const char *path __unused, const struct mach_header *mh)
{
if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
didInitialAttachCategories = true;
loadAllCategories();
}
// 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();
}
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_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;
}
在call_load_methods
中其实我们就可以看到类和分类的调用顺序了,类先于分类
再看call_class_loads
的实现
static void call_class_loads(void)
{
int i;
// Detach current loadable list.
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
(*load_method)(cls, @selector(load));
}
// Destroy the detached list.
if (classes) free(classes);
}
断点调试,
图片.png
会看到classes
里面包含了LGPerson
和LGTeacher
, 如果再多创建子类, 这里也会有变化
到这里就可以得出结论,
先
调用父类
的load方法, 然后是子类
的load方法
并且load
方法并不是使用的消息发送
objc_msgSend
而是直接指针调用