iOS-Developer-OCiOS - Developer - OC 进阶大全

Objct-C 中+load和+initialize

2019-08-01  本文已影响38人  zwwuchn

+load

@interfaceLPPerson:NSObject

@end

@implementationLPPerson

+ (void)load {

NSLog(@"LPPerson---+load");

}

@end

@interfaceLPPerson(Test1)

@end

@implementationLPPerson(Test1)

+ (void)load {

NSLog(@"LPPerson (Test1)---+load");

}

@end

@interfaceLPPerson(Test2)

@end

@implementationLPPerson(Test2)

+ (void)load {

NSLog(@"LPPerson (Test2)---+load");

}

@end

intmain(intargc,constchar* argv[]) {

@autoreleasepool{


    }

return0;

}

// 打印结果

LPPerson---+load

LPPerson (Test1)---+load

LPPerson (Test2)---+load

从上面代码可以看出:即使我们在我们没有调用LPPerson类、LPPerson分类也会打印。

打印一下类方法

这里我们要使用这个分类DLIntrospection
NSLog(@"%@",[[LPPersonclass] classMethods]);

// 打印结果

(

"+ (void)load",

"+ (void)load",

"+ (void)load"

)

说明类的类方法、类分类的类方法都在元类类方法列表中

下面,我们将结合runtime开源代码一起来揭开它们的神秘面纱。

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);
        // 加载load方法之间做一些准备(所有类的load方法调用顺序)
        prepare_load_methods((const headerType *)mh);
    }

    // 加载load方法
    call_load_methods();
}
void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertWriting();

    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);
    }
}
static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // 1.先将父类的load方法添加到loadable_list中
    schedule_class_load(cls->superclass);
    
    // 2.将cls添加到loadable_list最后面(放到最后面就说明最后调用load方法)
    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
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.首先调用类的load方法
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. 调用Category的load方法
        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;
}

首先调用类的load方法

我们查看怎么调用类的load方法call_class_loads()

static void call_class_loads(void)
{
    int i;
    
    // 存放所有加载的类数组,loadable_classes: 类数组内部存放类的位置,从而得到多个类之间的调用顺序
    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方法(load_method:指向类里面load方法的内存地址)
        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指针,直接调用:所以所有的load方法都会调用
        (*load_method)(cls, SEL_load);
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

+initialize

+initialize 方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用。也就是说 +initialize 方法是以懒加载的方式被调用的,如果程序一直没有给某个类或它的子类发送消息,那么这个类的 +initialize 方法是永远不会被调用的

我们来看一下runtime源代码

IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
                       bool initialize, bool cache, bool resolver)
{
    ...
        rwlock_unlock_write(&runtimeLock);
    }
    // 是否初始化 并且 这个类没有被初始化
    if (initialize  &&  !cls->isInitialized()) {
        _class_initialize (_class_getNonMetaClass(cls, inst));
        // 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
    }
    ...
}

当我们给某个类发送消息时,runtime 会调用这个函数在类中查找相应方法的实现或进行消息转发

当类没有初始化,会调用_class_getNonMetaClass方法
void _class_initialize(Class cls)
{
    ...
    Class supercls;
    BOOL reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // 父类进行递归调用,确保先初始化父类
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }

    // Try to atomically set CLS_INITIALIZING.
    monitor_enter(&classInitLock);
    if (!cls->isInitialized() && !cls->isInitializing()) {
        cls->setInitializing();
        reallyInitialize = YES;
    }
    monitor_exit(&classInitLock);

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

        // 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: calling +[%s initialize]",
                         cls->nameForLogging());
        }
        // runtime 使用了发送消息 objc_msgSend 的方式对 +initialize 方法进行调用
        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);

        if (PrintInitializing) {
            _objc_inform("INITIALIZE: finished +[%s initialize]",
    ...
}

走的都是发送消息的流程。换言之,如果子类没有实现 +initialize 方法,那么继承自父类的实现会被调用;如果一个类的分类实现了 +initialize 方法,那么就会先调用分类的+initialize 方法

总结

总结.png
上一篇下一篇

猜你喜欢

热点阅读