美文网首页
ObjC源码分析+initialize方法

ObjC源码分析+initialize方法

作者: tom555cat | 来源:发表于2018-11-16 18:32 被阅读18次

    ObjC中+initialize方法的规则:

    1. 惰性调用
    2. 只调用一次
    3. 执行时运行时系统处于正常状态
    4. 会先运行超类的+initialize方法
    5. 子类没有实现+initialize方法,会执行超类的+initialize方法
    6. 不要两个class的+initialize方法中互相使用对方,否则会造成初始化不完整

    从ObjC源码分析这些规则,首先在类EOCBaseClass中定义+initialize方法:

    #import "EOCBaseClass.h"
    
    @implementation EOCBaseClass
    
    + (void)initialize {
        NSLog(@"%@ initialize", self);
    }
    
    @end
    
    

    然后在main方法中创建EOCBaseClass对象

    #import <Foundation/Foundation.h>
    #import "EOCSubClass.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            EOCSubClass *sub = [[EOCSubClass alloc] init];
        }
        return 0;
    }
    

    然后在+initialize方法处打断点,运行程序:


    +initialize方法执行断点

    从调用栈来看,最开始是main()函数,在main()函数运行之前,动态库都已经完成了加在和初始化,class的+load方法均已调用完成。
    而且,在调用栈4::lookUpImpOrForward方法中会对接收消息涉及到的class执行realizeClass(cls)进行第一次初始化。所以说3. +initialize执行时运行时系统处于正常状态

    在调用栈6 _objc_msgSend_uncached,可以看出来只有当我们在向相关class发送消息的时候,才会最终调用+initialize,所以1. +initialize是惰性调用,只有在使用的时候才会调用

    查看lookUpImpOrForward源码,判断一个class的+initialize方法是否调用过是检查class对应的meta class的flag属性中RW_INITIALIZED(// class is initialized)标记位是否为YES,如果没有执行+initialize则为NO,在执行完+initialize之后标记位设置为YES,所以2. 只调用一次

    IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        ...
    
        if (initialize  &&  !cls->isInitialized()) {
            runtimeLock.unlockRead();
            _class_initialize (_class_getNonMetaClass(cls, inst));
            runtimeLock.read();
        }
        ...
    }
    
    bool isInitialized() {
        return getMeta()->data()->flags & RW_INITIALIZED;
    }
    

    再看_class_initialize方法,_class_initialize中会递归地调用_class_initialize(cls->supercls)实现先执行超类的initialize方法,然后执行子类的initialize方法。所以4. 会先运行超类的+initialize方法

    /***********************************************************************
    * class_initialize.  Send the '+initialize' message on demand to any
    * uninitialized class. Force initialization of superclasses first.
    **********************************************************************/
    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);
        }
        ...
    
        if (reallyInitialize) {
            ...
            {
                callInitialize(cls);
    
                if (PrintInitializing) {
                    _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                                 pthread_self(), cls->nameForLogging());
                }
            }
        }
        ...
    }
    
    
    void callInitialize(Class cls)
    {
        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
        asm("");
    }
    

    我们在父类EOCBaseClass中定义了+initialize方法,子类EOCSubClass中没有定义+initialize方法,在main()函数中我们创建的是子类对象,根据对规则3的分析,在调用堆栈3 ::_class_initialize是对子类EOCSubClass调用,堆栈2 ::_class_initialize是对父类EOCSubClass调用。
    在_class_initialize通过callInitialize(cls),在其内部是通过"((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);"调用+initialize方法的,也就是说子类调用的+initialize方法是父类定义的同名方法。所以最后执行结果为:

    EOCBaseClass initialize
    EOCSubClass initialize
    

    所以解释了规则5. 子类没有实现+initialize方法,会执行超类的+initialize方法

    对于规则6.不要两个class的+initialize方法中互相使用对方,在EOCClassA的+initialize中使用EOCClassB,在EOCClassB的+initialize中使用EOCClassA;如果main()函数中使用EOCClassA,则+initialize调用看起来是这样的:

    EOCClassA_initialize->EOCClassB_initialize->EOCClassA_initialize
    

    然后在EOCClassA在执行+initialize方法时,EOCClassA的isInitializing()设置为true,而在上述循环调用中再次进入EOCClassA的+initialize中,发现其isInitializing()为true,就直接返回了,所以此时EOCClassB的+initialize方法并没有获得一个完整的EOCClassA。

    相关文章

      网友评论

          本文标题:ObjC源码分析+initialize方法

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