load和initialize都是NSObject的类方法
NSObject.h 声明如下
+ (void)load;
+ (void)initialize;
NSObject.mm 实现如下
+ (void)load {
}
+ (void)initialize {
}
可以看到默认都是空实现。
-
先看一下load方法的调用
在Objective-C 源码中看一下load_images的实现,在objc-runtime-new.mm中
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
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();
}
可以看到会先调用prepare_load_methods方法,再调用call_load_methods方法。
1.1 prepare_load_methods方法的实现
void prepare_load_methods(const headerType *mhdr)
{
size_t count, I;
runtimeLock.assertWriting();
// class
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}
// category
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 class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
可以看到class和category是分开处理的。
1.1.1 schedule_class_load方法实现
/***********************************************************************
* prepare_load_methods
* Schedule +load for classes in this image, any un-+load-ed
* superclasses in other images, and any categories in this image.
**********************************************************************/
// Recursively schedule +load for cls and any un-+load-ed superclasses.
// cls must already be connected.
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);
}
可以看到会通过cls->superclass递归调用,确保父类先加入list
1.1.1.1 add_class_to_loadable_list方法实现
/***********************************************************************
* 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) {
// Don't bother if cls has no +load method
return;
}
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++;
}
看一下loadable_classes的定义
// List of classes that need +load called (pending superclass +load)
// This list always has superclasses first because of the way it is constructed
static struct loadable_class *loadable_classes = nil;
static int loadable_classes_used = 0;
static int loadable_classes_allocated = 0;
1.1.2 add_category_to_loadable_list方法实现
/***********************************************************************
* add_category_to_loadable_list
* Category cat's parent class exists and the category has been attached
* to its class. Schedule this category for +load after its parent class
* becomes connected and has its own +load method called.
**********************************************************************/
void add_category_to_loadable_list(Category cat)
{
IMP method;
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat);
if (!method) {
// Don't bother if cat has no +load method
return;
}
if (PrintLoading) {
_objc_inform("LOAD: category '%s(%s)' scheduled for +load",
_category_getClassName(cat), _category_getName(cat));
}
if (loadable_categories_used == loadable_categories_allocated) {
loadable_categories_allocated = loadable_categories_allocated*2 + 16;
loadable_categories = (struct loadable_category *)
realloc(loadable_categories,
loadable_categories_allocated *
sizeof(struct loadable_category));
}
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
loadable_categories_used++;
}
1.2 call_load_methods的实现
/***********************************************************************
* call_load_methods
* Call all pending class and category +load methods.
* Class +load methods are called superclass-first.
* Category +load methods are not called until after the parent class's +load.
*
* This method must be RE-ENTRANT, because a +load could trigger
* more image mapping. In addition, the superclass-first ordering
* must be preserved in the face of re-entrant calls. Therefore,
* only the OUTERMOST call of this function will do anything, and
* that call will handle all loadable classes, even those generated
* while it was running.
*
* The sequence below preserves +load ordering in the face of
* image loading during a +load, and make sure that no
* +load method is forgotten because it was added during
* a +load call.
* Sequence:
* 1. Repeatedly call class +loads until there aren't any more
* 2. Call category +loads ONCE.
* 3. Run more +loads if:
* (a) there are more classes to load, OR
* (b) there are some potential category +loads that have
* still never been attempted.
* Category +loads are only run once to ensure "parent class first"
* ordering, even if a category +load triggers a new loadable class
* and a new loadable category attached to that class.
*
* Locking: loadMethodLock must be held by the caller
* All other locks must not be held.
**********************************************************************/
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;
}
1.2.1 call_class_loads方法实现
/***********************************************************************
* call_class_loads
* Call all pending class +load methods.
* If new classes become loadable, +load is NOT called for them.
*
* Called only by call_load_methods().
**********************************************************************/
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, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
方法调用可以看到是通过(*load_method)(cls, SEL_load)直接调用函数指针方式实现的
1.2.2 call_category_loads方法实现
/***********************************************************************
* call_category_loads
* Call some pending category +load methods.
* The parent class of the +load-implementing categories has all of
* its categories attached, in case some are lazily waiting for +initalize.
* Don't call +load unless the parent class is connected.
* If new categories become loadable, +load is NOT called, and they
* are added to the end of the loadable list, and we return TRUE.
* Return FALSE if no new categories became loadable.
*
* Called only by call_load_methods().
**********************************************************************/
static bool call_category_loads(void)
{
int i, shift;
bool new_categories_added = NO;
// Detach current loadable list.
struct loadable_category *cats = loadable_categories;
int used = loadable_categories_used;
int allocated = loadable_categories_allocated;
loadable_categories = nil;
loadable_categories_allocated = 0;
loadable_categories_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Category cat = cats[i].cat;
load_method_t load_method = (load_method_t)cats[i].method;
Class cls;
if (!cat) continue;
cls = _category_getClass(cat);
if (cls && cls->isLoadable()) {
if (PrintLoading) {
_objc_inform("LOAD: +[%s(%s) load]\n",
cls->nameForLogging(),
_category_getName(cat));
}
(*load_method)(cls, SEL_load);
cats[i].cat = nil;
}
}
// Compact detached list (order-preserving)
shift = 0;
for (i = 0; i < used; i++) {
if (cats[i].cat) {
cats[i-shift] = cats[I];
} else {
shift++;
}
}
used -= shift;
// Copy any new +load candidates from the new list to the detached list.
new_categories_added = (loadable_categories_used > 0);
for (i = 0; i < loadable_categories_used; i++) {
if (used == allocated) {
allocated = allocated*2 + 16;
cats = (struct loadable_category *)
realloc(cats, allocated *
sizeof(struct loadable_category));
}
cats[used++] = loadable_categories[I];
}
// Destroy the new list.
if (loadable_categories) free(loadable_categories);
// Reattach the (now augmented) detached list.
// But if there's nothing left to load, destroy the list.
if (used) {
loadable_categories = cats;
loadable_categories_used = used;
loadable_categories_allocated = allocated;
} else {
if (cats) free(cats);
loadable_categories = nil;
loadable_categories_used = 0;
loadable_categories_allocated = 0;
}
if (PrintLoading) {
if (loadable_categories_used != 0) {
_objc_inform("LOAD: %d categories still waiting for +load\n",
loadable_categories_used);
}
}
return new_categories_added;
}
方法调用可以看到是通过(*load_method)(cls, SEL_load)直接调用函数指针方式实现的
load 方法调用总结
load方法是在添加到runtime时开始执行,先执行父类load 方法,再执行子类load方法,最后是类别中的load方法。因为是通过函数指针方式实现调用的,所以不会想objc_msgSend那样有方法查找过程。
2 initialize方法的调用
在Objective-C 源码中看一下_class_initialize的实现,在objc-initialize.mm中
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
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;
}
}
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
// Record that we're initializing this class so we can message it.
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
//
// Only __OBJC2__ adds these handlers. !__OBJC2__ has a
// bootstrapping problem of this versus CF's call to
// objc_exception_set_functions().
#if __OBJC2__
@try
#endif
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
pthread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
// We couldn't set INITIALIZING because INITIALIZING was already set.
// If this thread set it earlier, continue normally.
// If some other thread set it, block until initialize is done.
// It's ok if INITIALIZING changes to INITIALIZED while we're here,
// because we safely check for INITIALIZED inside the lock
// before blocking.
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
return;
} else {
// We're on the child side of fork(), facing a class that
// was initializing by some other thread when fork() was called.
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
else if (cls->isInitialized()) {
// Set CLS_INITIALIZING failed because someone else already
// initialized the class. Continue normally.
// NOTE this check must come AFTER the ISINITIALIZING case.
// Otherwise: Another thread is initializing this class. ISINITIALIZED
// is false. Skip this clause. Then the other thread finishes
// initialization and sets INITIALIZING=no and INITIALIZED=yes.
// Skip the ISINITIALIZING clause. Die horribly.
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
我们可以看到通过递归调用,也是确保父类先执行。
下面看一下执行方法
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
通过代码可以知道initialize方法是通过objc_msgSend来执行的,会经过方法查找流程。
initialize 方法调用总结
initialize 方法是在收到第一条消息前调用的,initialize是通过objc_msgSend来执行的,会经过方法查找流程,先执行父类中的initialize方法再执行子类中的方法但是类别中的方法会覆盖类中的方法。
3 示例
3.1 load
DMFunctionLoadGrandSonModel继承自DMFunctionLoadSonModel,DMFunctionLoadSonModel继承自DMFunctionLoadFatherModel。DMFunctionLoadFatherModel的类别DMLoad,DMFunctionLoadSonModel的类别DMLoad和DMLoadTwo,DMFunctionLoadGrandSonModel的类别DMLoad和DMLoadTwo,都实现load方法只是输出log。如下
通过图片可以看到是先输出DMFunctionLoadFatherModel中的log,然后是DMFunctionLoadSonModel中的log,接着输出DMFunctionLoadGrandSonModel中的log,最后才是类别中的log,类别中log输出的顺序和工程设置中编译顺序有关。
3.2 initialize
DMFunctionInitializeGrandSonModel继承自DMFunctionInitializeSonModel,DMFunctionInitializeSonModel继承自DMFunctionInitializeFatherModel,先都实现initialize方法只是输出log。触发方式如下
DMFunctionInitializeGrandSonModel *grandSonModel = [[DMFunctionInitializeGrandSonModel alloc] init];
输出log如下:
可以看到是先输出DMFunctionInitializeFatherModel中的log,然后是DMFunctionInitializeSonModel中的log,最后才是DMFunctionInitializeGrandSonModel中的log。
现在去掉DMFunctionInitializeSonModel中的initialize实现,触发方式不变,输出log如下:
可以看到两次输出DMFunctionInitializeFatherModel中的log,最后才是DMFunctionInitializeGrandSonModel中的log。这是因为DMFunctionInitializeSonModel中的initialize没有实现,但是initialize是通过objc_msgSend来执行的,会经过方法查找流程,所以会再执行一次父类DMFunctionInitializeFatherModel中的initialize,进而又输出了一遍DMFunctionInitializeFatherModel中的log。
现在恢复DMFunctionInitializeSonModel中的initialize实现,添加DMFunctionInitializeSonModel的类别DMInitialize并实现initialize方法,触发方式不变。输出如下
可以看到先输出DMFunctionInitializeFatherModel中的log,然后是DMFunctionInitializeSonModel类别DMInitialize中的log,最后才是DMFunctionInitializeGrandSonModel中的log。由此可见类别中initialize的实现会覆盖类中的initialize实现。
继续添加DMFunctionInitializeSonModel的类别DMInitializeTwo并实现initialize方法,触发方式不变。输出如下
可以看到先输出DMFunctionInitializeFatherModel中的log,然后是DMFunctionInitializeSonModel类别DMInitializeTwo中的log,最后才是DMFunctionInitializeGrandSonModel中的log。至于输出的是DMFunctionInitializeSonModel类别DMInitializeTwo中的log而不是DMFunctionInitializeSonModel类别DMInitialize中的log,是根据文件编译顺序决定的,后编译的文件方法会在方法列表的前面,而objc_msgSend消息查找只要找到方法就会停止继续查找,所以会找到方法列表前面的。