Runtime03load()和initialize()的加载顺序

demo项目的目录结构

在这里插入图片描述

上代码:

main.m (啥都没写):

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    return 0;
}

Person类:

@interface Person : NSObject
@end
//---------分割线,分隔一个类的.h文件和.m文件-------

#import "Person.h"
@implementation Person
+ (void)load {
    NSLog(@"Person load");
}
+ (void)initialize
{
    NSLog(@"Person initialize()");
}
@end

Person+Eat类(即Person类的分类):

#import "Person.h"
@interface Person (Eat)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------

#import "Person+Eat.h"
@implementation Person  (Eat)
+ (void)load {
    NSLog(@"Person(Eat) load");
}
+ (void)initialize
{
    NSLog(@"Person(Eat) initialize()");
}
@end

Person+Test类(即Person类的分类):

#import "Person.h"
@interface Person (Test)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------

#import "Person+Test.h"
@implementation Person  (Test)
+ (void)load {
    NSLog(@"Person(Test) load");
}
+ (void)initialize
{
    NSLog(@"Person (Test) initialize()");
}
@end

Student类:

#import "Person.h"
@interface Student : Person
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Student.h"
@implementation Student
+ (void)load {
    NSLog(@"Student load");
}
+ (void)initialize
{
    NSLog(@"Student initialize()");
}
@end

Student+Test1类(即Student类的分类):

#import "Student.h"
@interface Student (Test1)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------

#import "Student+Test1.h"
@implementation Student  (Test1)
+ (void)load {
    NSLog(@"Student(Test1) load");
}
+ (void)initialize
{
    NSLog(@"Student (Test1) initialize()");
}
@end

Student+Test类(即Student类的分类):

#import "Person.h"
@interface Student (Test2)
@end
//---------分割线,分隔一个类的.h文件和.m文件-------

#import "Student+Test2.h"
@implementation Student  (Test2)
+ (void)load {
    NSLog(@"Student(Test2) load");
}
+ (void)initialize
{
    NSLog(@"Student (Test2) initialize()");
}
@end

Cat类:

@interface Cat : NSObject
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Cat.h"
@implementation Cat
+ (void)load {
    NSLog(@"Cat load");
}
+ (void)initialize
{
    NSLog(@"Cat initialize()");
}
@end

Dog类:

@interface Dog : NSObject
@end
//---------分割线,分隔一个类的.h文件和.m文件-------
#import "Dog.h"
@implementation Dog
+ (void)load {
    NSLog(@"Dog load");
}
+ (void)initialize
{
    NSLog(@"Dog initialize()");
}
@end

总结一下上面代码的继承和分类的关系:在这里插入图片描述

运行结果与分析

在这里插入图片描述
从代码和打印结果可以看出,即使main.m文件没有导入Person或者Student等类或者分类的头文件,这些类的load()都会被调用,说明程序开始运行时,代码被加载进内存后,所以类和分类的load()就会被执行。当这些load()执行完成后,main()函数才开始被执行。注意:demo执行完了,所有类的initialize()都不会被调用。因为在该demo的运行过程中,没有类接收到消息。一个类接收到消息指的是在代码里面调用 [类名 方法名]; 或者调用 objc_msgSend(类对象或者实例对象,方法的SEL);

调整类和分类的编译顺序,打印结果如下:

在这里插入图片描述

在上面的demo代码的基础上,往main.m文件的添加一些头文件和一个方法调用

此时的main.m文件的代码如下:

#import <Foundation/Foundation.h>
#import "Dog.h" //添加的内容
#import "Student.h" //添加的内容

int main(int argc, const char * argv[]) {
    @autoreleasepool { //添加的内容
        [Student alloc]; //添加的内容
    } //添加的内容
    return 0;
}

运行结果和分析

[Student alloc];表示向Student类发送alloc消息。当一个类在第一次接收到消息时,该类的initialize方法就会被调用。此后,当该类第二次或第二次以上接收到消息时,该类的initialize方法就不再会被调用。

在这里插入图片描述

上一张图的编译顺序如下图:
在这里插入图片描述

load方法调用的源码解析:

先看load方法加载的整体的时序图
在这里插入图片描述

objc-os.mm文件

void _objc_init(void) //程序刚开始运行时被调用(该函数在main函数被调用之前被调用)
{
    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();
	//map_images和load_images都是一个函数。当_dyld_objc_notify_register函数被调用时,map_images函数会先被调用,然后才调用load_images函数。
	//map_images函数用于构造所有的类实例及其元类实例 以及 这些类实例及其元类实例的方法列表、协议列表、属性列表和成员变量列表等。即map_images函数调用过后,一个程序的每一个类实例及其元类实例的方法列表、协议列表、属性列表和成员变量列表等都已经有该类(包括其对应的分类)的所有方法、所有协议、所有属性和成员变量了。
	//load_images函数内部会先调用每一个类的load函数。等到所有的类的load函数被调用完成后,所有类的分类的load函数才会被调用。所有类的load函数的调用顺序是根据编译顺序来决定,先编译的类的load函数就会先被调用。同理,先被编译的分类的load函数也会先被调用。
	//注意:当一个类B继承父类A时,虽然类B在父类A之前被编译,但是因为是父子关系,所以父类A的load函数先被调用完之后,然后类B的load函数才会被调用。
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

上一篇博客已经分析了map_images函数里面的调用过程,所以现在分析load_images函数里面的调用过程。

objc-runtime-new.mm文件

/*

struct loadable_class {  //结构体
    Class cls;  // may be nil,类对象  
    IMP method; //类对象对应的函数指针
};
static struct loadable_class *loadable_classes = nil; //loadable_class数组
*/
//程序刚开始运行时,该函数会在main函数调用之前被调用
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); //把每个类都封装成loadable_class结构体,然后将这些loadable_class结构体实例放到同一个数组中。
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods(); //调用所有类及其分类的load函数
}

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();

    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i])); //该函数是个递归函数
    }

    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);
    }
}

/*

struct loadable_class {  //结构体
    Class cls;  // may be nil,类对象  
    IMP method; //类对象对应的函数指针
};
static struct loadable_class *loadable_classes = nil; //loadable_class数组
*/
 //该函数是个递归函数。该函数的作用是先把父类作为参数传入到schedule_class_load函数中,然后再把子类作为参数传入到schedule_class_load函数中。
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return; //如果父类已经被添加过了(添加到loadable_classes数组中了),则直接返回而不会被重复添加了(不会再次被添加到loadable_classes数组中)

    // Ensure superclass-first ordering
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}

/*
struct loadable_class {  //结构体
    Class cls;  // may be nil,类对象  
    IMP method; //类对象对应的函数指针
};
*/
//把一个类封装成loadable_class结构体,再将该结构体添加到loadable_classes数组中。该数组会在后面的函数中被读取,所以先记住loadable_classes数组是在此时被初始化的。
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++;
}

objc-loadmethod.mm

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) {  //loadable_classes_used是在上面的add_class_to_loadable_list函数中被赋值,该变量表示loadable_classes数组的长度
            call_class_loads(); //1、先调用所有的类的load函数(根据编译顺序调用,有继承关系的例外,即如果有继承关系,则父类的load函数先被调用完成,子类的load函数才会被调用)
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();  //2、然后才调用所有的分类的load函数(根据编译顺序调用)

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

/*
typedef void(*load_method_t)(id, SEL);  //load_method_t是一个函数指针

struct loadable_class {  //结构体
    Class cls;  // may be nil,类对象  
    IMP method; //类对象对应的函数指针,具体指的是load函数的指针
};

*/
//调用类的load方法,具体是通过函数指针直接调用load函数,而不是通过消息发送与接收流程来调用每一个类的load方法
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++) { //遍历前面已经初始化号的loadable_classes数组,该数组元素类型是loadable_class结构体类型,该结构体包含了一个类的实例对象的指针及其load函数的指针
        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); //通过函数指针直接调用load函数,所以不是通过消息发送与接收流程来调用每一个类的load方法。
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

//调用分类的load方法,也是通过函数指针直接调用load函数,而不是通过消息发送与接收流程来调用每一个分类的load方法。
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); //通过函数指针直接调用load函数,所以不是通过消息发送与接收流程来调用每一个类的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;
}

initialize方法调用的源码分析

先看load方法加载的整体的时序图

在这里插入图片描述

[Student alloc];等价于objc_msgSend([Student class], @selector(alloc) );,表示向Student类发送alloc消息。initialize方法是在objc_msgSend()中被调用的。但objc_msgSend()是由汇编实现的。下图是从runtime源码工程里面搜索的,发现搜出来的objc_msgSend()都是由汇编实现。那方法调用既然是通过objc_msgSend()来实现向对象发送消息,那肯定会涉及到对被调用方法的查找过程,即从类对象或者元类对象的方法列表里面查找目标方法。而查找目标方法则是通过调用class_getInstanceMethod()或者class_getClassMethod()函数来实现。
在这里插入图片描述

class_getClassMethod()的源码调用流程如下:

/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel); //调用class_getInstanceMethod(),第1个参数是类的元类对象
}

class_getClassMethod()内部是调用class_getInstanceMethod(),所以只需要分析class_getInstanceMethod()的源码,该函数的源码如下:

Method class_getInstanceMethod(Class cls, SEL sel) //sel指的是你要调用的方法
{
    if (!cls  ||  !sel) return nil;

    // This deliberately avoids +initialize because it historically did so.

    // This implementation is a bit weird because it's the only place that 
    // wants a Method instead of an IMP.

#warning fixme build and search caches
        
    // Search method lists, try method resolver, etc.
    lookUpImpOrNil(cls, sel, nil, 
                   NO/*initialize*/, NO/*cache*/, YES/*resolver*/); //查找所调用的实例或者类方法

#warning fixme build and search caches

    return _class_getMethod(cls, sel);
}


/***********************************************************************
* lookUpImpOrNil.
* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
**********************************************************************/
IMP lookUpImpOrNil(Class cls, SEL sel, id inst, 
                   bool initialize, bool cache, bool resolver)
{
    IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);  //查找所调用的实例或者类方法
    if (imp == _objc_msgForward_impcache) return nil;
    else return imp;
}

/***********************************************************************
* lookUpImpOrForward.
* The standard IMP lookup. 
* initialize==NO tries to avoid +initialize (but sometimes fails)
* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)
* Most callers should use initialize==YES and cache==YES.
* inst is an instance of cls or a subclass thereof, or nil if none is known. 
*   If cls is an un-initialized metaclass then a non-nil inst is faster.
* May return _objc_msgForward_impcache. IMPs destined for external use 
*   must be converted to _objc_msgForward or _objc_msgForward_stret.
*   If you don't want forwarding at all, use lookUpImpOrNil() instead.
**********************************************************************/
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    // runtimeLock is held during isRealized and isInitialized checking
    // to prevent races against concurrent realization.

    // runtimeLock is held during method search to make
    // method-lookup + cache-fill atomic with respect to method addition.
    // Otherwise, a category could be added but ignored indefinitely because
    // the cache was re-filled with the old value after the cache flush on
    // behalf of the category.

    runtimeLock.lock();
    checkIsKnownClass(cls);

    if (!cls->isRealized()) {
        realizeClass(cls);
    }

    if (initialize  &&  !cls->isInitialized()) { //如果类对象没有初始化(即没有调用过该类或者其分类的initialize方法)
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst)); //调用类的initialize方法
        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
    }

    
 retry:    
    runtimeLock.assertLocked();

    // Try this class's cache.

    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists.
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    // Try superclass caches and method lists.
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Halt if there is a cycle in the superclass chain.
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }
            
            // Superclass cache.
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Found a forward:: entry in a superclass.
                    // Stop searching, but don't cache yet; call method 
                    // resolver for this class first.
                    break;
                }
            }
            
            // Superclass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    // No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        runtimeLock.unlock();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.lock();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }

    // No implementation found, and method resolver didn't help. 
    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlock();

    return imp;
}


//递归函数,如果父类的initialize方法没有被调用,则先调用父类的,然后才调用子类的initialize方法。
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()) { //如果父类存在并且父类的initialize方法没有被调用过
        _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);  //调用当前类的initialize方法

            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!");
    }
}

//调用当前类的initialize方法,里面是通过objc_msgSend()函数调用的,但该函数是汇编实现的。
void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

load方法和initialize方法的相同点:

  1. 都是类方法;
  2. 都是系统默认调用;
  3. 父类的load先被调用,子类的load才被调用。同理,父类的initialize先被调用,子类的initialize才被调用。

load方法和initialize方法的区别:

  1. 调用方式:load方式是通过函数指针直接被调用。而initialize方法则是通过消息发送机制被调用;

  2. 调用时机和覆盖问题:所有类及其分类的load方法都是在程序刚开始运行时并且在main函数执行之前就会被调用,不存在被分类的load方法的覆盖问题,即load方法的调用顺序是 父类->子类->分类。而initialize方法是在方法所属的类在第一次接收到消息时才会被调用,类第一次接收到消息指的是你第一次通过 [类名 方法名]或者objc_msgSend(类对象, SEL)来向类发送消息,类的initialize方法会被其分类的initialize方法覆盖(如果类的分类也实现了initialize方法的话)。

  3. 父类的调用次数:父类的load方法只能被调用一次。但是父类的initialize方法可能被调用多次(前提是子类的initialize方法没有被实现)

    举例:
    ①假如B类和C类都是A类的子类,且CategoryA是A的分类,A、B、C和CategoryA都实现了initialize方法,则当你调用[B alloc]; [C alloc]时,先分析[B alloc]调用之后会发生:CategoryA的initialize方法会先被调用(A的initialize方法不会被调用,即A的initialize方法被CategoryA分类覆盖了,因为CategoryA的initialize方法是在A类的元类的方法列表里面的排在A类的initialize方法前面),然后调用B的initialize方法,最后[C alloc]才会被调用,此时才会调用C类的initialize方法。
    ②假如B类和C类都是A类的子类,且CategoryA是A的分类,只有A和CategoryA实现了initialize方法,则当你调用[B alloc]; [C alloc]时,先分析[B alloc]调用之后会发生:CategoryA的initialize方法会先被调用(A的initialize方法不会被调用,即A的initialize方法被CategoryA分类覆盖了,因为CategoryA的initialize方法是在A类的元类的方法列表里面的排在A类的initialize方法前面),此时Runtime在B的元类找不到initialize方法,所以Runtime会通过B的元类的super指针找到A的元类,然后在A的元类的方法列表里面找到了CategoryA实现的initialize方法(在A的元类的方法列表里面,CategoryA的initialize方法排在A的initialize方法前面),然后调用CategoryA实现的initialize方法,最后[C alloc]才会被调用,同理,此时Runtime在C的元类找不到initialize方法,所以Runtime会通过C的元类的super指针找到A的元类,然后在A的元类的方法列表里面找到了CategoryA实现的initialize方法,然后调用CategoryA实现的initialize方法。所以,在这个例子中,当你调用[B alloc]; [C alloc]时,即先想B发送了alloc消息,然后想C发送了alloc消息,Runtime最终调用3次了CategoryA的initialize方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值