美文网首页
Objc源码之initialize实现

Objc源码之initialize实现

作者: 繁星mind | 来源:发表于2019-07-02 09:48 被阅读0次

Objc源码之对象创建alloc和init
Objc源码之initialize实现
Objc源码之Load方法实现
Objc源码之NSObject和isa
Objc源码之引用计数实现
objc源码之Method消息发送

前言

   提到initialize方法,我们总会拿它和load方法作对比,那是因为这两个方法都是系统调用,且比较靠前,可以再类还没初始化的时候,做一些事情,关于两者的调用时机和差异点,大家可以看下我的这两篇文章iOS中load和initialize方法总结,还有Runtime源码之Load方法实现,这篇文章主要来分析initialize方法的源码实现。

  接下来,我会从源码的角度,来说明一下几个问题:

  • initialize方法调用时机
  • initialize的调用顺序

注:本文分析基于objc4-750源码进行的。

一、initialize方法调用时机

1.initialize方法调用栈

首先,我们创建一个对象TestObject,添加initialize方法,并在initialize方法中打上断点,这时我们可以看到下图的调用栈,我们可以看到initialize函数是通过lookUpImpOrForward调用,并最终调用_class_initialize函数,来调用方法中的initialize函数,调用栈如下图所示:

initialize调用栈
由上面的调用栈,我们可以知道,initialize的调用是在main函数的后面执行,在方法调用的_objc_msgSend_uncached时候,需要调用方法查找函数lookUpImpOrForward,而lookUpImpOrForward里面会调用_class_initialize进行初始类,最终调用initialize。下面,我们就重点分析一下lookUpImpOrForward_class_initialize的源码实现。
2.lookUpImpOrForward和_class_initialize源码实现

首先我们看下lookUpImpOrForward源码实现:

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    runtimeLock.assertUnlocked();

    // 1.在缓存中查找方法
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    runtimeLock.lock();
    checkIsKnownClass(cls);
    
   
    if (!cls->isRealized()) {
        realizeClass(cls);
    }

    //2.类是否初始化过
    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
    }
    省略......
}

我们知道lookUpImpOrForward是在调用方法时,进行消息转发的时候,会调用的,再调用具体方法的时候,会先判断类是否初始化过,通过cls->isInitialized()来进行判断,如果还没有被初始化过,就会调用_class_initialize来调用initialize方法。

    bool isInitialized() {
        return getMeta()->data()->flags & RW_INITIALIZED;
    }

类的初始化信息,记录在元类class_rw_t 结构体中的 flags 中,所以这里cls->isInitialized()是通过读取flags值来判断是否初始化过的。

如果没有初始化过,那么就调用_class_initialize方法,这是调用initialize和信方法:

void _class_initialize(Class cls)
{
    assert(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // 1.判断父类是否初始化
    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }
    
    // 2.判断是否已经初始化或者正在初始化,设置CLS_INITIALIZING
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }
    }
    
    if (reallyInitialize) {        
        // 3.记录初始化类的线程,只有这个这个线程可以给类发送消息,其他线程等待
        _setThisThreadIsInitializingClass(cls);
        //4.子线程fork时父进程是多线程的,执行fork子线程的Initialize
        if (MultithreadedForkChild) {
            performForkChildInitialize(cls, supercls);
            return;
        }

        {
            //5.调用Initialize方法
            callInitialize(cls);
        }
        {
            // 6.设置Initialized
            lockAndFinishInitializing(cls, supercls);
        }
        return;
    }
    //7.正在初始化
    else if (cls->isInitializing()) {
  
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else if (!MultithreadedForkChild) {
            waitForInitializeToComplete(cls);
            return;
        } else {
            _setThisThreadIsInitializingClass(cls);
            performForkChildInitialize(cls, supercls);
        }
    }
    //8.已经初始化 返回
    else if (cls->isInitialized()) {
        return;
    }
    
    else {
        // We shouldn't be here. 
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}

1)判断父类是否初始化,如果没有初始化,递归调用父类初始化。

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

2)判断类是否已经初始化或者正在初始化,设置CLS_INITIALIZING

        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }

3)记录初始化类的线程,只有这个这个线程可以给类发送消息,其他线程等待,这个在第七步的时候,_thisThreadIsInitializingClass会用到。

_setThisThreadIsInitializingClass(cls);

4)子线程fork时父进程是多线程的,执行fork子线程的Initialize

if (MultithreadedForkChild) {
     performForkChildInitialize(cls, supercls);
     return;
 }

5)调用callInitialize方法,执行Initialize方法

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

6)设置Initialized, 如果父类已经初始化完成,设置标志位。否则,在父类初始化完成之后再设置标志位.

lockAndFinishInitializing(cls, supercls);
static void lockAndFinishInitializing(Class cls, Class supercls)
{
    monitor_locker_t lock(classInitLock);
    if (!supercls  ||  supercls->isInitialized()) {
        _finishInitializing(cls, supercls);
    } else {
        _finishInitializingAfter(cls, supercls);
    }
}

7)类正在初始化,判断类在哪个线程初始化,如果在当前线程初始化(通过第3步记录的值判断),直接返回,如果在多线程不是fork的,等待初始化完成,如果是子线程fork时父进程是多线程的,执行performForkChildInitialize,和4)相同的操作。

if (_thisThreadIsInitializingClass(cls)) {
    return;
} else if (!MultithreadedForkChild) {
    waitForInitializeToComplete(cls);
    return;
} else {
    _setThisThreadIsInitializingClass(cls);
    performForkChildInitialize(cls, supercls);
}

8)如果已经初始化成功,直接返回

 else if (cls->isInitialized()) {
        return;
    }

二、initialize的调用顺序

_class_initialize中的可以看出来,父类是优先于子类调用。

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

initialize执行顺序总结

1.如果类和类Category都实现了initialize方法,调用Category的initialize方法,会覆盖类中的方法,只执行一个,如果多个category,则调用编译顺序最后的initialize方法。

2.如果父类和子类都实现了initialize方法,
在调用子类时,如果父类的initialize方法调用过,则只调用子类initialize方法。
如果父类没用过,则先调用父类的Category的initialize方法,在调用子类的initialize方法
如果调用子类的时候,已经初始化过父类的initialize,则在初始化父类的时候,不会再调用initialize方法

3.如果子类没有实现initialize方法,父类实现了initialize方法,调用子类的时候,会先调用父类的initialize方法,再调用子类的实例方法。

总结:initialized方法是在第一次向类发送消息时调用的,通过lookUpImpOrForward最终调用到callInitialize的,从整个过程可以看出来initialize是多线程安全的,并且保证父类优先于子类调用。

参考:

objc4-750源码
nsobject-load-and-initialize-what-do-they-do
iOS中load和initialize方法总结
懒惰的 initialize 方法.md

相关文章

网友评论

      本文标题:Objc源码之initialize实现

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