+initialize方法的原理

2020-03-19  本文已影响0人  85ca4232089b

initialize源码分析。objc源码下载地址

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;
    Class curClass;

    runtimeLock.assertUnlocked();

    // 小概率事件
    if (fastpath(behavior & LOOKUP_CACHE)) {
        imp = cache_getImp(cls, sel);
        if (imp) goto done_nolock;
    }
    runtimeLock.lock();

    checkIsKnownClass(cls);
// 很可能 cls 是有值的,编译器可以不用每次都读取 return nil 指令(小概率事件)
    if (!cls->isRealized()) {
        realizeClass(cls);
    }
  if (slowpath(!cls->isRealized())) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }

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

首先我们会发现 initialize 方法出现在 lookUpImpOrForward方法中.
• lookUpImpOrForward 方法是在方法查找转发流程当中,这也就说明了initialize方法是在main函数之后调用的

  1. 会进行判断当前的cls有没有实现initialize方法,
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.lock();
    }

• if中的条件是大概率(fastpath:真值判断)还是小概率(slowpath:假值判断)事件,从而让编译器对代码进行优化

Class _class_getNonMetaClass(Class cls, id obj)
{
    rwlock_writer_t lock(runtimeLock);
    cls = getNonMetaClass(cls, obj);
    assert(cls->isRealized());
    return cls;
}
static Class getNonMetaClass(Class metacls, id inst)
{
    static int total, named, secondary, sharedcache;
    runtimeLock.assertLocked();

    realizeClass(metacls);

    total++;

    // return cls itself if it's already a non-meta class
    if (!metacls->isMetaClass()) return metacls;

    // metacls really is a metaclass

    // special case for root metaclass
    // where inst == inst->ISA() == metacls is possible
    if (metacls->ISA() == metacls) {
        Class cls = metacls->superclass;
        assert(cls->isRealized());
        assert(!cls->isMetaClass());
        assert(cls->ISA() == metacls);
        if (cls->ISA() == metacls) return cls;
    }
}

• _class_getNonMetaClass :给一个非元类对象(类),发送了一个类方法

  1. 如果不是元类,则不应该走下面的逻辑,直接返回
    if (!metacls->isMetaClass()) return metacls;
  2. 如果是元类的元类是自己,如果它的superclass的isa指向自己,则表示是根元类
  3. 然后获取非元类的实例,且不获取子类
    if (inst) {
        Class cls = (Class)inst;
        realizeClass(cls);
        // cls may be a subclass - find the real class for metacls
        while (cls  &&  cls->ISA() != metacls) {
            cls = cls->superclass;
            realizeClass(cls);
        }
        if (cls) {
            assert(!cls->isMetaClass());
            assert(cls->ISA() == metacls);
            return cls;
        }
    }

• 如果没有实现_class_initialize 递归遍历cls父类

void _class_initialize(Class cls)
{
    supercls = cls->superclass;
    //如果cls有父类,且父类没有+initialize过。递归调用
    if (supercls  &&  !supercls->isInitialized()) {
        //递归调用
        _class_initialize(supercls);
    }
...
@try
    {
        callInitialize(cls);
    }
    @catch (...) {
        ...
    }
    @finally
    {
        // 设置该类flag标示该类已经调用+initialize初始化过
        lockAndFinishInitializing(cls, supercls);
    }
}

• 给该cls设置flag标示该cls已经调用+initialize初始化过

      lockAndFinishInitializing(cls, supercls);

• callInitialize(Class cls) 通过消息机制调用initialize方法。
消息发送机制:遍历类的继承关系,依次从子类到父类的方法缓存列表和方法列表中寻找方法,未找到时进行动态方法解析,未能解析是走消息转发机制)

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

initialize类方法的特点:

• 即使类文件被引用进项目,但是没有使用,initialize不会被调用
• 假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。
• 类或者其子类的第一个方法被调用前调用
• 由于是系统自动调用,也不需要再调用 [super initialize] ,否则父类的initialize会被多次执行

initialize类方法调用的特点

• 父类的initialize方法会比子类先执行
• 子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法.
• 当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)

总结:

• load和initialize方法都会在实例化对象之前调用
• load执行在main函数以前,initialize执行main函数之后
• 这两个方法会被自动调用,不能手动调用它们。
• load和initialize方法都不用显示的调用父类的方法而是自动调用
• 子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类
• initialize方法对一个类而言只会调用一次(Person、或者是Person+Category)都是一个Perosn类。load方法则是每个都会调用,只要你写了load方法,添加到工程都会实现。
• load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。
• load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。
load方法解析

上一篇下一篇

猜你喜欢

热点阅读