美文网首页
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