initialized

  • +initialized方法会在类第一次接收到消息时调用
  • 调用顺序
    • 先调用父类的+initialize,再调用子类的+initialize
    • 先初始化父类,再初始化子类,每个类只会初始化1次
  • +initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以有以下特点
    • 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)
    • 如果分类实现了+initialize,就覆盖类本身的+initialize调用

objc4源码解读

  • objc-msg-arm64.s,汇编级别的源码
    • objc_msgSend,消息发送汇编源码
  • objc-runtime-new.mm
    • class_getInstanceMethod
    • lookUpImpOrNil
    • lookUpImpOrForward
    • _class_initialize
    • callInitialize
    • objc_msgSend(cls, SEL_initialize)

源码分析,objc-runtime-new.mm文件

class_getInstanceMethod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/***********************************************************************
* class_getInstanceMethod. Return the instance method for the
* specified class and selector.
**********************************************************************/
Method class_getInstanceMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;

// This deliberately avoids +initialize because it historically did so.

// This implementation is a bit weird because it's the only place that
// wants a Method instead of an IMP.

#warning fixme build and search caches

// Search method lists, try method resolver, etc.
// 查找方法
lookUpImpOrNil(cls, sel, nil,
NO/*initialize*/, NO/*cache*/, YES/*resolver*/);

#warning fixme build and search caches

return _class_getMethod(cls, sel);
}

lookUpImpOrNil

1
2
3
4
5
6
7
8
9
10
11
/***********************************************************************
* lookUpImpOrNil.
* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
**********************************************************************/
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
if (imp == _objc_msgForward_impcache) return nil;
else return imp;
}

lookUpImpOrForward

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;

runtimeLock.assertUnlocked();

// 省去代码...

// 判断是否初始化过类,没有就进行初始化
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();

// 初始化操作
_class_initialize (_class_getNonMetaClass(cls, inst));

runtimeLock.read();
// 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
}


}

_class_initialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void _class_initialize(Class cls)
{
// 省去代码...

{
// 调用自己的初始化方法
callInitialize(cls);

if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}

// 省去代码...
}

callInitialize

1
2
3
4
5
6
void callInitialize(Class cls)
{
// 消息发送机制
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}

高频面试

  • Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?

    • 有load方法
    • load方法在runtime加载类、分类的时候调用
    • load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用
  • load、initialize方法的区别什么?它们在category中的调用的顺序?以及出现继承时他们之间的调用过程?

    • 分别在两个笔记里面顶部体现
  • Category能否添加成员变量?如果可以,如何给Category添加成员变量?

    • 不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果

参考