+initialize方法的原理
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函数之后调用的
- 会进行判断当前的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 :给一个非元类对象(类),发送了一个类方法
- 如果不是元类,则不应该走下面的逻辑,直接返回
if (!metacls->isMetaClass()) return metacls; - 如果是元类的元类是自己,如果它的superclass的isa指向自己,则表示是根元类
- 然后获取非元类的实例,且不获取子类
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方法解析