load函数应该知道的事情
一、load介绍
类和分类的load方法是类或者分类被加载到runtime时被调用,并且只调用一次。类和分类的load方法都会调用。而“类或者分类被加载到runtime时”时动态链接阶段,此时main函数还未执行。
load
方法的运行时间过早,所以这里可能不是一个理想的环境,因为某些类可能需要在在其它类之前加载,但是这是我们无法保证的。但是,framework以及动态库是先加载的,所有库加载完成之后,才会执行运行时的load方法,所以在load方法中调用 framework 中的方法都是安全的。
二、load底层实现
看756版本的runtime源码,可以看到load_images洪处理了load相关逻辑,下面我们仔细研读一下源码
/***********************************************************************
* load_images
* Process +load in the given images which are being mapped in by dyld.
*
* Locking: write-locks runtimeLock and loadMethodLock
**********************************************************************/
extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
//1、判断镜像中是否有load方法,没有load方法,返回
if (!hasLoadMethods((const headerType *)mh)) return;
//加一个递归锁
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
//2、查找所有的load方法
{
mutex_locker_t lock2(runtimeLock);
//准备load方法
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
//3、调用所有的load方法
call_load_methods();
}
通过上面的源码,我们可以总结load_images处理所有的load方法的过程:
先判断镜像中是否load方法(通过hasLoadMethods方法来实现)
- 如果有,查找所有的load方法(prepare_load_methods实现),并且调用这些load方法(通过call_load_methods实现)
- 如果没有,直接返回
第一阶段:查找所有的类和分类的load方法
1、hasLoadMethods方法
// Quick scan for +load methods that doesn't take a lock.
//快速查找load方法
bool hasLoadMethods(const headerType *mhdr)
{
size_t count;
if (_getObjc2NonlazyClassList(mhdr, &count) && count > 0) return true;
if (_getObjc2NonlazyCategoryList(mhdr, &count) && count > 0) return true;
return false;
}
hasLoadMethods方法通过_getObjc2NonlazyClassList() 和 _getObjc2NonlazyCategoryList()来查找类或者分类中是否有load方法,只有任何一个有load方法,说明需要处理load方法。
2、相关方法
我们了解到hasLoadMethods通过_getObjc2NonlazyClassList() 和 _getObjc2NonlazyCategoryList()来查找类或者分类是否有load方法,下面我们看看这两个方法的实现
//__objc_nlclslist表示类的load方法列表,__objc_nlcatlist表示分类的load方法列表
// function name content type section name
GETSECT(_getObjc2NonlazyClassList, classref_t, "__objc_nlclslist");
GETSECT(_getObjc2NonlazyCategoryList, category_t *, "__objc_nlcatlist");
#define GETSECT(name, type, sectname) \
type *name(const headerType *mhdr, size_t *outC