美文网首页
OC +initialize方法调用时机与顺序(源码分析)

OC +initialize方法调用时机与顺序(源码分析)

作者: 再好一点点 | 来源:发表于2021-10-25 09:27 被阅读0次

想搞明白initialize就需要从id objc_msgSend(id self, SEL _cmd, ...);入手,但是此函数使用汇编实现,很多东西看不明白,但是根据里边的注释可以看到调用方法拿到IMP会使用IMP objc_msgLookup(id self, SEL _cmd, ...);。然后最终可以确定其实最终调用的是这个函数Method class_getInstanceMethod(Class cls, SEL sel)。下面根据这个函数开始分析。

一. 调用顺序
1. Method class_getInstanceMethod(Class cls, SEL sel)
Method class_getInstanceMethod(Class cls, 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);
}
2. 调用 IMP lookUpImpOrNil(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)
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;
}
3. 调用 IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    ......

    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlockRead();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.read();
        // 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
    }
  
    ......  

    return imp;
}
4. 调用 void _class_initialize(Class cls)

这里是个递归,在调用一个类的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()) {
        _class_initialize(supercls);
    }

    ......
        {
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                             pthread_self(), cls->nameForLogging());
            }
        }
    ......
}
5. 调用 void callInitialize(Class cls)
void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

二. 如果调用者不是元类对象

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

 // NOT identical to this->ISA when this is a metaclass
 Class getMeta() 
     if (isMetaClass()) return (Class)this;
     else return this->ISA();
 }

如果不是元类对象就返回该对象的isa,确保返回元类对象。

总结调用顺序:

class_getInstanceMethod -> lookUpImpOrNil -> lookUpImpOrForward -> _class_initialize -> callInitialize

相关文章

网友评论

      本文标题:OC +initialize方法调用时机与顺序(源码分析)

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