iOS 底层系列 - load 和 initialize 方法

1,744 阅读5分钟

一、前言

此篇文章主要介绍的底层系列 - load 和 initialize,对于 load 和 initialize 对象我们大多数人都了解,但是对于底层和很多知识点总是容易忘。

二、问题

load方法是什么时候调用的?

  • load方法,在runtime加载类、分类的时候调用。

load 方法能继承吗?

  • Load 方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用load。

load、initialize方法的区别什么?

  • +initialize

    • 先调用父类,再调用子类(如果分类实现了+initialize,就 覆盖类本身的 +initialize调用),在类第一次收到消息的时候调用(是通过objc_msgSend进行调用的)
  • +load

    • 方法是根据方法地址直接调用,并不是经过 objc_msgSend 函数调用。是 通过函数指针指向函数,拿到函数地址,分开来直接调用的(直接通过内存地址查找调用的)

load、initialize方法的特点

  • 如果子类没有实现 +initialize,会调用父类的 +initialize(所以父类的 +initialize可能会被调用多次)
  • 如果分类实现了 +initialize,就覆盖类本身的 +initialize调用。

三、总结

除了普通类是按照继承必须先调用父类的 super 之外,其它类都是按照编译顺序调用 +load 的。

+load

+initialize

四、相关源码 + load

1.load_images

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

        // 准备 load 方法
        prepare_load_methods((const headerType *)mh);
    }
    // Call +load methods (without runtimeLock - re-entrant)
    // 调用 load 方法
    call_load_methods();
}

2.prepare_load_methods

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

    // 拿到类的编译顺序
    classref_t const *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);

    // 1. 递归调用,添加父类,和子类
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    // 2. 添加分类的 loadlist
    category_t * const *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
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

3. add_category_to_loadable_list

void add_category_to_loadable_list(Category cat)
{
    IMP method;
    loadMethodLock.assertLocked();
    method = _category_getLoadMethod(cat);
    // Don't bother if cat has no +load method
    if (!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++;
}

五、相关源码 + initialize

1. initializeAndMaybeRelock

static Class initializeAndMaybeRelock(Class cls, id inst,
                                      mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();
    ASSERT(cls->isRealized());

    // 如果已经调用,那么返回
    if (cls->isInitialized()) {
        if (!leaveLocked) lock.unlock();
        return cls;
    }
     initializeNonMetaClass(nonmeta);
}

2. initializeNonMetaClass

// class_initialize.  Send the '+initialize' message on demand to any
// uninitialized class. Force initialization of superclasses first.

void initializeNonMetaClass(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.

    // 首先调用父类 的 initialize
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }

    callInitialize(cls);
}

3. callInitialize

// 通过消息发送的方式
void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}