load ,initialize 有啥区别
调用方式的区别 : load 是根据函数地址直接调用initialize 是通过_objc_msgSend 调用
load 是runtime在加载类和分类的时候调用只会调用一次
initialize 是类第一次接收到消息的时候调用 每一个类只会 initialize 一次 但是父类会调用多次
load 是类在加载到内存的时候 被调用的 如果特别想在这个时候做什么可以调用
直入主题上代码
按照以往的调用顺序,category中的方法应该是在最先调用的才对。但是现在每一个类的load都有被调用这是为啥?
首先从 runtime初始化方法入手 runtime源码 objc-os.mm文件中 _objc_init()方法开始
load_images 加载镜像文件 ,通过名字可见 是要找的东西 jump进去
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();
}
call_load_methods();
调用 load 方法 进去看看
官方注释超级清楚
-
Call all pending class and category +load methods.
调用所有class 和 category 的 +load 方法 -
Class +load methods are called superclass-first.
类 的+load 方法 是超类 优先调用 -
Category +load methods are not called until after the parent class’s +load.
category 的+load 方法 在类的 +load方法之后调用 -
Repeatedly call class +loads until there aren’t any more
重复调用class +load,直到没有更多的 -
Call category +loads ONCE.
调用类别+加载一次。
在看代码
loadable_classes_used
/***********************************************************************
* add_class_to_loadable_list
* Class cls has just become connected. Schedule it for +load if
* it implements a +load method.
**********************************************************************/
void add_class_to_loadable_list(Class cls)
{
IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod();
if (!method) return; // Don't bother if cls has no +load method
if (PrintLoading) {
_objc_inform("LOAD: class '%s' scheduled for +load",
cls->nameForLogging());
}
if (loadable_classes_used == loadable_classes_allocated) {
loadable_classes_allocated = loadable_classes_allocated*2 + 16;
loadable_classes = (struct loadable_class *)
realloc(loadable_classes,
loadable_classes_allocated *
sizeof(struct loadable_class));
}
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
loadable_classes_used++;
}
`while (loadable_classes_used > 0) {
call_class_loads();
}` 如果有实现了 +(void)load 方法就会调用 call_class_loads()
jump进去
便利取出类里边的load方法,load_method 直接指向load方法的内存地址
typedef void(*load_method_t)(id, SEL);
直接指向函数地址
(*load_method)(cls, SEL_load);
然后调用
调用分类的load方法
便利分类直接调用
为什么 load 三个都会调用,是因为 实现里,是通过遍历方法列表里的方法,直接拿到函数指针去调用函数 不是通过 objc_msgSend()调用(只要是通过发消息调用 就会产生顺序调用)。
load调用顺序: 所有类的load 方法走完才走 分类的
类级别越高越先调用 平级的类 是通过编译顺序来控制 调用 load的顺序
分类的load 调用 不分先后 可以通过编译顺序来控制
编译顺序控制方法
+initialize
initialize 这个东西干啥的 我理解的就是 你特别想在这个类第一次使用的时候干些什么事 在你的类接收到消息之前会调用一个这个东西来初始化。也不知道对不对,实际开发中我没用过这个玩应。就连调用load 都屈指可数。
-
initialize方法会在类第一次接收到消息的时候调用
-
如果有父类会点调用父类的 initialize 再调用子类的 initialize
虽然没用过但是也没准哪天就有人问 谁知道呢。
说一下我的查找思路,大致了解过runtime的 都说第一次接收消息都时候调用 也就是说应该从objc_msgSend开始查 但是这玩应是汇编写的,同时我又知道
- 当汇编在缓存查找方法 如果找到会直接Call
- CacheMiss 就会走到这里
然后发现了一个 initialize
return lookUpImpOrForward(cls, sel, obj, YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
initialize 给传过来的是YES
if (initialize && !cls->isInitialized()) {
runtimeLock.unlock();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.lock();
// If sel == initialize, _class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen. 2778172
}
那这里就顺过来了。
if (YES && 类没有初始化){
_class_initialize (_class_getNonMetaClass(cls, inst));
调用初始化方法
}
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
- 如果存在父类 并且父类没初始化 就去初始父类
- 父类初始化完继续执行然后吧自己也初始化
这是终极目标
谨以此记录学习的点点滴滴