美文网首页
+load +initialize的一点见解

+load +initialize的一点见解

作者: 小白猿 | 来源:发表于2019-04-09 14:45 被阅读0次

整体比较

比较项目 +load +initialize
调用次数 1次 多次
是否需要显式调用父类实现
是否沿用父类的实现
调用顺序 父类->子类->分类 分类->父类->子类
调用时机 被添加到 runtime 时 收到第一条消息前,可能永远不调用
分类中的实现 类和分类都执行 覆盖类中的方法,只执行分类的实现

对方法执行顺序的一点见解

这两个方法的执行顺序和一般方法的执行顺序都有些差异,按照普通的方法调用顺序,如果方法funcA子类父类中都实现了,在子类的实例对象中调用funcA的话,会执行子类的实现,如果是分类中也实现了funcA,那么优先执行分类中的实现

但是 +load 方法和 +initialize 中父类和子类的循序调用都是调用父类,调用子类,通过源码得知,实际上是采用了递归的方法,优先将父类指针传入,即起到优先调用父类中的实现,说通俗一点就是在方法的实现中,会先调用一遍父类的方法,这一点+load 方法和 +initialize 实现方式是一样

但是分类的方法调用顺序二者就不一样了,+load方法将分类的实现放到了最后+initialize是沿用了普通方法的调用,优先调用分类中的实现,这一点我们不去细究,认为其实遵循了基本的调用顺序,那么+load方法为什么能最后调用分类中的方法呢?参看源码得知:实际上是优先遍历了父类和子类的方法之后,并且运用递归保证父类在子类之前调用,然后在全部调用完毕以后,再去遍历分类中的方法,进行分类调用
以上被划掉的解释不够严谨,甚至错误的,+initialize方法调用底层是通过objc_sendMsg即使用的消息转发机制,所以分类的调用必然要要比原类先调用,但是+load并非使用的是这种方式,其使用通过方法指针的方式直接调用,故不受消息转发机制的限制,自定义调用顺序

源码摘录如下
打开 runtime 工程,我们接下来看看与 +load 方法相关的几个关键函数。首先是文件 objc-runtime-new.mm 中的 void prepare_load_methods(header_info *hi) 函数

void prepare_load_methods(header_info *hi)
{
    size_t count, i;

    rwlock_assert_writing(&runtimeLock);

    classref_t *classlist =
        _getObjc2NonlazyClassList(hi, &count);
    for (i = 0; i < count; i++) {
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t **categorylist = _getObjc2NonlazyCategoryList(hi, &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);
    }
}

然后跳入核心方法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;

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

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

方法实现中自己调用了自己,并且传入了父类的指针
schedule_class_load(cls->superclass);即起到优先调用父类的方法,然后打开文件 objc-loadmethod.m ,找到其中的 void call_load_methods(void)函数。

void call_load_methods(void)
{
    static BOOL loading = NO;
    BOOL more_categories;

    recursive_mutex_assert_locked(&loadMethodLock);

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

这里主要看do-while循环内部,do内部也有一个while循环,优先处理类中方法call_class_loads();,在内层的while外边处理分类中的方法more_categories = call_category_loads();

然后在+initialize的源码中也有类似递归调用,保证父类执行的顺序的代码,但是对于分类中没有做特殊的处理

void _class_initialize(Class cls)
{
    ...
    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_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());
        }

        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);

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

主要是这里

  supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }

由于最近几年一直接触的是Swift 的项目,对于OC的一些研究渐渐减少,造成一些概念的模糊,现在想重新拾起,对与文章中错误请各位大神指正

文章参考链接
Objective-C +load vs +initialize

相关文章

网友评论

      本文标题:+load +initialize的一点见解

      本文链接:https://www.haomeiwen.com/subject/msibiqtx.html