类初始化的时机
每次调用类或是对象的方法的时候,会转换成消息发送,调用类或对象的方法其实就是给类或对象发送消息,
发送消息是通过objc_megSend()系列的方法,然后会调用 objc-runtime-new.mm 文件里面的lookUpImpOrForward()方法,这个方法会检测类是否是已经初始化过的状态(initialized),如果没有类没有初始化,则去初始类。
lookUpImpOrForward()方法部分代码片段如下
//如果类没有初始化,则初始化类
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
//#define fastpath(x) (__builtin_expect(bool(x), 1))
//#define slowpath(x) (__builtin_expect(bool(x), 0))
//__builtin_expect作用是"允许程序员将最有可能执行的分支告诉编译器"。这个指令的写法为:__builtin_expect(EXP, N)。意思是:EXP==N的概率很大。
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// runtimeLock may have been dropped but is now locked again
// 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方法,而且是由messenger(一般是我们自己调用)调用,会调用两次,但是第一次调用initialize就会把类标记为initialized
}
可以看到,当类没有标记为initialized方法的时候,会去调用initializeAndLeaveLocked()方法, initializeAndLeaveLocked()方法最终会调用initializeAndMaybeRelock()方法
static Class initializeAndMaybeRelock(Class cls, id inst,
mutex_t& lock, bool leaveLocked)
{
lock.assertLocked();
ASSERT(cls->isRealized());
if (cls->isInitialized()) {//如果已经执行过了初始化方法,则不调用
if (!leaveLocked) lock.unlock();
return cls;
}
// Find the non-meta class for cls, if it is not already one.
// The +initialize message is sent to the non-meta class object.
Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// Realize the non-meta class if necessary.
if (nonmeta->isRealized()) {
// nonmeta is cls, which was already realized
// OR nonmeta is distinct, but is already realized
// - nothing else to do
lock.unlock();
} else {
nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
// runtimeLock is now unlocked
// fixme Swift can't relocate the class today,
// but someday it will:
cls = object_getClass(nonmeta);
}
// runtimeLock is now unlocked, for +initialize dispatch
ASSERT(nonmeta->isRealized());
initializeNonMetaClass(nonmeta);//执行初始化方法
if (leaveLocked) runtimeLock.lock();
return cls;
}
// Locking: caller must hold runtimeLock; this may drop and re-acquire it
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
return initializeAndMaybeRelock(cls, obj, lock, true);
}
在initializeAndMaybeRelock()方法会判断类是否已经初始化,如果没有初始化,会调用initializeNonMetaClass()方法去初始化类,并标记类为initialized
类初始化流程
从上可知,类是通过initializeNonMetaClass()去初始化一个类的,initializeNonMetaClass()代码如下
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
初始化类
**********************************************************************/
void initializeNonMetaClass(Class cls)
{
ASSERT(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
//优先执行父类的initial方法
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
// Try to atomically set CLS_INITIALIZING.
//设置类正在初始化,并加锁
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
monitor_locker_t lock(classInitLock);//尝试对这个类进行加锁,加锁成功后才会去设置为状态为正在初始化
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
// Grab a copy of the will-initialize funcs with the lock held.
localWillInitializeFuncs.initFrom(willInitializeFuncs);
}
}
if (reallyInitialize) {
//正常进入初始化
// We successfully set the CLS_INITIALIZING bit. Initialize the class
// Record that we're initializing this class so we can message it.
//标记当前线程正在初始化该类
_setThisThreadIsInitializingClass(cls);
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
for (auto callback : localWillInitializeFuncs)
callback.f(callback.context, cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
#if __OBJC2__
@try
#endif
{
//假设子类没有实现initialize方法,会调用父类的initialize方法
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
objc_thread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
objc_thread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
//标记初始化完成
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
//类正在初始化
//如果该类正在初始化,而且是当前线程在初始化,则不执行任何操作
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
//等待线程初始化
return;
} else {
//如果是别的线程正在初始化该类
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
else if (cls->isInitialized()) {
//如果该类已经被初始化了,就不执行任何操作
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
在initializeNonMetaClass()的开始,有一个对该方法的递归调用
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
这里的作用是当类存在父类的时候,会去优先初始化父类。当类初始化完成的时候,再一层一层的往下初始化类。
这里有个问题,类什么时候算是初始化完成?记住,当一个类被标记为initialized状态的时候,类就算是初始化完成,而不是等到类的+initialize()方法执行完才算是初始化完成。
一个类关于初始化有三个状态,未初始化,正在初始化(nitializing),初始化完成(initialized)。
初始化的时候会根据类的初始化状态设置reallyInitialize变量,当reallyInitialize变量的值为YES,就进入到真正的初始化,调用callInitialize()方法,去调用我们定义的+ initialize()方法,然后再调用lockAndFinishInitializing()方法,将类设置为初始化完成状态initialized。
callInitialize()方法定义如下
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
这个方法中也是通过消息发送机制objc_msgSend去调用我们定义的+ initialize()方法。因为是通过消息发送机制去调用的,所以当一个类没有定义+ initialize()方法的时候,会调用父类的+ initialize()方法,所以一个类的+ initialize()方法有可能会被调用多次。但是只有在第一次调用+ initialize()才会把类的初始化状态设置为initialized。
执行完以后就要通过调用lockAndFinishInitializing()设置类为initilaized,实现如下
static void lockAndFinishInitializing(Class cls, Class supercls)
{
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(cls, supercls);
}
}
static void _finishInitializing(Class cls, Class supercls)
{
PendingInitialize *pending;
classInitLock.assertLocked();
ASSERT(!supercls || supercls->isInitialized());
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: %s is fully +initialized",
objc_thread_self(), cls->nameForLogging());
}
// mark this class as fully +initialized
cls->setInitialized();//标志为inititaled状态
classInitLock.notifyAll();//释放锁
_setThisThreadIsNotInitializingClass(cls);
// mark any subclasses that were merely waiting for this class
if (!pendingInitializeMap) return;
auto it = pendingInitializeMap->find(cls);
if (it == pendingInitializeMap->end()) return;//如果pendingInitializeMap只有一个元素,则直接返回·
pending = it->second;
pendingInitializeMap->erase(it);
// Destroy the pending table if it's now empty, to save memory.
if (pendingInitializeMap->size() == 0) {
delete pendingInitializeMap;
pendingInitializeMap = nil;
}
//递归调用,将相同父类而且完成了初始化方法,但是状态未被标记成Initialied的类标记成Initialied
while (pending) {
PendingInitialize *next = pending->next;
if (pending->subclass) _finishInitializing(pending->subclass, cls);
delete pending;
pending = next;
}
}
static void _finishInitializingAfter(Class cls, Class supercls)
{
classInitLock.assertLocked();
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: class %s will be marked as fully "
"+initialized after superclass +[%s initialize] completes",
objc_thread_self(), cls->nameForLogging(),
supercls->nameForLogging());
}
if (!pendingInitializeMap) {
pendingInitializeMap = new PendingInitializeMap{10};
// fixme pre-size this table for CF/NSObject +initialize
}
PendingInitialize *pending = new PendingInitialize{cls};
auto result = pendingInitializeMap->try_emplace(supercls, pending);
if (!result.second) {
pending->next = result.first->second;
result.first->second = pending;
}
}
lockAndFinishInitializing()方法中有一个判断 (!supercls || supercls->isInitialized()),判断是否不存在父类或是存在父类且父类已经完成初始化。
- 如果是,则调用_finishInitializing(),_finishInitializing()的作用是标记类完成初始化。
- 否则调用_finishInitializingAfter()。
前面不是说类的初始化initializeNonMetaClass()方法通过递归调用,可以确保父类会优先执行吗?怎么会出现子类执行lockAndFinishInitializing()的时候父类还没完成初始化的情况呢?
其实根据面向对象的思想,父类的定义中是不存在关于任何关于子类的内容的,如下
一般情况
。
但是有些特殊情况,会在父类的+initialize()方法中去调用子类的方法,如下
QQ20200627-213128.png
当我们执行[[ClassB alloc] init]方法的时候,会想ClassB发送消息,调用ClassB的initializeNonMetaClass(),接着会调用ClassA的initializeNonMetaClass(),在Class A中调用子类Class C的alloc方法,就是向子类Class C发送消息,由于子类Class C还没有初始化,所以会调用initializeNonMetaClass(),如下图中的 调用1。
然后就又回递归调用,一直调用到ClassA的initializeNonMetaClass()方法。如下图中的 调用2
这时候执行到Class A的initializeNonMetaClass()方法,由于ClassA正在初始化(当执行第一次initializeNonMetaClass()方法的时候,会把当前类的初始化状态设置为),会跳到
(cls->isInitializing()) {
//类正在初始化
// We couldn't set INITIALIZING because INITIALIZING was already set.
// If this thread set it earlier, continue normally.
// If some other thread set it, block until initialize is done.
// It's ok if INITIALIZING changes to INITIALIZED while we're here,
// because we safely check for INITIALIZED inside the lock
// before blocking.
//如果该类正在初始化,而且是当前线程在初始化,则不执行任何操作
if (_thisThreadIsInitializingClass(cls)) {
return;
} else if (!MultithreadedForkChild) {
waitForInitializeToComplete(cls);
//等待线程初始化
return;
} else {
//如果是别的线程正在初始化该类
// We're on the child side of fork(), facing a class that
// was initializing by some other thread when fork() was called.
_setThisThreadIsInitializingClass(cls);
performForkChildInitialize(cls, supercls);
}
}
然后会判断是否是当前线程在初始化这个类,由于在前面我们是通过递归调用的,所以是在同一个线程(为什么需要判断是否是当前线程?由于设计到多线程,等会继续说一下这里的处理)。所以_thisThreadIsInitializingClass()成立,就直接返回。
这时候就会回到ClassB的initializeNonMetaClass()方法 ,如下图 调用3。这时候由于之前的ClassB没有初始化过,reallyInitialize为YES,这时候就正常进入到初始化流程。
QQ20200627-223549.png先调用callInitialize()方法,然后再调用lockAndFinishInitializing()方法,这时候注意了,ClassA正处于 正在初始化(initializing)的阶段,在调用ClassB的lockAndFinishInitializing()方法的时候,由于父类还没初始化完成,就会_finishInitializingAfter()方法,
_finishInitializingAfter()方法不会将类的状态设置为initalized,只会把当前类存入到pendingInitializeMap中。pendingInitializeMap是一个存放那些完成了初始化方法,但是父类还没标记成完成初始化状态initalized的类的容器。
ClassC也和ClassB一样,由于ClassC的初始化状态不是initalized,所以会执行callInitialize()方法,然后再调用_finishInitializingAfter()方法,把类B存入到pendingInitializeMap中
最后回到ClassA的initializeNonMetaClass()方法,并调用lockAndFinishInitializing()方法,由于ClassA没有父类,所以最终会调用_finishInitializing()方法。_finishInitializing()方法中先是把ClassA的状态设置为initalized。然后通过递归在pendingInitializeMap找到以ClassA为父类的ClassB,并把ClassB的状态设置为initalized。再把以ClassB为父类的ClassC设置为initalized。
从上面可以看出,ClassA的+initialize()方法在ClassB的+initialize()前执行,ClassB的+initialize()方法在ClassC的+initialize()前执行。这样就确保了一条规则
- 父类的+initialize()无论在什么情况下都会比子类的+initialize()优先执行。
但是从上面也可以看出,ClassA的+initialize()方法在还没有执行完毕的时候,ClassB和ClassC的+initialize()已经执行完毕了。所以会有以下的一条规则
- 当父类的+initialize()调用了子类的方法的时候,子类的+initialize()会比父类的+initialize()方法先执行完毕
多线程与初始化
在initializeNonMetaClass()方法中涉及到了一些线程处理,在类的初始化阶段为什么需要线程操作呢?
先来看以下的情况
QQ20200627-234732.png
存在ClassA,ClassB,ClassC,ClassD,ClassE五个类,继承关系如上图所示
在不考虑线程处理的情况下,执行代码
NSThread *t1 = [[NSThread alloc] initWithBlock:^{
[[ClassC alloc] init];
}];
NSThread *t2 = [[NSThread alloc] initWithBlock:^{
[[ClassD alloc] init];
}];
[t2 start];
[t1 start];
开启两个线程,初始化ClassC 和 ClassD,会出现什么问题呢?
这个问题有可能出现死锁,为什么呢?
从类的初始化 流程可知,当父类+initiale()方法中有调用子类的方法的时候,递归调用initializeNonMetaClass()是会出现环形的。
当我们在线程1向ClassD发送消息的时候,会调用initializeNonMetaClass()方法。然后这时候另外一个线程2向ClassC发送消息,然后调用initializeNonMetaClass()了,ClassC的initializeNonMetaClass()方法调用了ClassB的initializeNonMetaClass(),ClassB的initializeNonMetaClass()调用了ClassA的initializeNonMetaClass()。
这时候ClassA中的+initialze()方法中会向ClassE发送消息,从而调用ClassE的initializeNonMetaClass()方法。而ClassE的initializeNonMetaClass()会调用ClassD的initializeNonMetaClass()方法
线程2中的ClassE需要等到线程1中的ClassD初始化完成才能继续走下去。而线程1中的ClassD需要等到线程2中的ClassB初始化完成才能走下去,而线程2中的ClassB需要等到等到ClassE中初始化完成才走下去。这样,就形成了死锁,如下
QQ20200628-001224.png
那objc库是怎么解决这个问题呢?objc中是通过加锁去保证当前线程初始化的连续性。连续性是当一个线程对类初始化的时候,类本身和类的所有负类的初始化工作全部交由该线程处理。别的线程如果向初始化该类或是父类,都会阻塞。
在不考虑线程优先级的情况下,当两个不同的线程去初始化具有相同父类的类的时候,先执行初始化流程的线程会保证初始化的连续性。为什么呢?
因为初始化方法initializeNonMetaClass()方法,会通过递归方式去调用父类的initializeNonMetaClass()。当递归条件不成立,也就是类已经没有父类的情况下(根类)或是父类已经初始化完成的情况下。才会进入到初始化的执行阶段。
void initializeNonMetaClass(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.
//优先执行父类的initial方法
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
// Try to atomically set CLS_INITIALIZING.
//设置类正在初始化,并枷锁
SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;
{
monitor_locker_t lock(classInitLock);//尝试对这个类进行加锁,加锁成功后才会去设置为状态为正在初始化
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
// Grab a copy of the will-initialize funcs with the lock held.
localWillInitializeFuncs.initFrom(åwillInitializeFuncs);
}
}
...正式进入初始化工作...
}
从代码的实现可知,在正式进入初始化工作前,会对代码进行加锁
monitor_locker_t lock(classInitLock)
如果线程一旦加锁成功,在完成初始化并释放掉锁之前。别的线程就没有办法去加锁,就会阻塞,就是没有办法进入初始化流程。那么该线程就可以处理类的初始化流程并不受别的线程打断。
换言之,就是哪个线程可以优先使没有初始化的父类进入到正在初始化的阶段,那么该线程就会负责该类的所有负类的初始化工作,并不受别的线程影响
其他
当线程调用初始化方法initializeNonMetaClass()并进入reallyInitialize条件时,就会执行callInitialize()方法,调用我们定义的+initialze()方法。
但在callInitialize()前,会执行_setThisThreadIsInitializingClass()方法,这个方法的作用是把当前类添加到当前线程的_objc_initializing_classes中。
_objc_initializing_classes是指该线程正在初始化的类的集合。
网友评论