源码解读
在objc-runtime-new.mm中找到class_getInstanceMethod方法
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.
lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
#warning fixme build and search caches
return _class_getMethod(cls, sel);
}
进入lookUpImpOrNil方法
lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)
{
return lookUpImpOrForward(obj, sel, cls, behavior | LOOKUP_CACHE | LOOKUP_NIL);
}
进入lookUpImpOrForward方法 条件!cls->isInitialized() 类未初始化
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
...
// 重点 isInitialized
if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
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
}
...
}
进入initializeAndLeaveLocked
// 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方法
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
* inst is an instance of cls, or nil. Non-nil is better for performance.
* Returns the class pointer. If the class was unrealized then
* it may be reallocated.
* Locking:
* runtimeLock must be held by the caller
* This function may drop the lock.
* On exit the lock is re-acquired or dropped as requested by leaveLocked.
**********************************************************************/
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;
}
进入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;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
// 递归操作 所以会无限调用父类
if (supercls && !supercls->isInitialized()) {
initializeNonMetaClass(supercls);
}
// 调用初始化方法
callInitialize(cls);
}
进入callInitialize方法 initialize走的是class_msgSend的消息转发方法
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
asm("");
}
例子
期望结果:
- 子类未初始化initilize的时候会调用父类的initilize,不同子类会有可能都调用,但真正初始化只有第一次
- initialize的源码回归操作说明先父类如果未初始化先调用父类
- initialize走的是class_msgSend的消息转发方法,所以会先调用分类的方法,而且先编译后调用
MJStudent,MJTeacher的父类为MJPerson,这里MJPerson实现initialize方法
@implementation MJPerson
+ (void)initialize
{
NSLog(@"MJPerson +initialize");
}
@end
@implementation MJStudent
@end
@implementation MJTeacher
@end
[MJStudent alloc]; // MJStudent首先初始父类的initialize方法,MJStudent未实现initialize方法,则调用父类
[MJTeacher alloc]; MJTeacher未实现initialize方法,则调用父类
打印结果:
-load[15503:456856] MJPerson +initialize
-load[15503:456856] MJPerson +initialize
-load[15503:456856] MJPerson +initialize
给MJStudent, MJTeacher添加initialize方法
@implementation MJStudent
+ (void)initialize
{
NSLog(@"MJStudent +initialize");
}
@end
@implementation MJTeacher
+ (void)initialize
{
NSLog(@"MJTeacher +initialize");
}
@end
[MJStudent alloc]; // 会先调用MJPerson的initialize,后调用MJStudent的initialize
[MJTeacher alloc]; // MJPerson已经initialize,只调用MJTeacher的initialize
[MJPerson alloc]; // MJPerson已经initialize
打印结果:
-load[15297:447570] MJPerson +initialize
-load[15297:447570] MJStudent +initialize
-load[15297:447570] MJTeacher +initialize
MJStudent分类添加Test1,并且实现initialize方法
@implementation MJStudent(Test1)
+ (void)initialize
{
NSLog(@"MJStudent (Test1) +initialize");
}
@end
[MJStudent alloc]; // 会先调用MJPerson的initialize,后调用MJStudent分类的initialize
[MJTeacher alloc]; // MJPerson已经initialize,只调用MJTeacher的initialize
[MJPerson alloc]; // MJPerson已经initialize
打印结果:
-load[15331:449685] MJPerson +initialize
-load[15331:449685] MJStudent (Test1) +initialize
-load[15331:449685] MJTeacher +initialize
总结
-
+initialize方法会在类第一次接收到消息时调用
-
调用顺序,先调用父类,再调用子类,有分类先调用分类
-
源码解读顺序
1.objc-runtime-new.mm
2.class_getInstanceMethod
3.lookUpImpOrForward
4._class_initialize
5.callInitialize
6.objc_msgSend(cls, SEL_initialize)
-
load和initlize的区别
1.调用方式
load通过函数地址直接调用
initialize通过objc_msgSend调用
2.调用时刻
load是runtime加载类,分类时候调用(只会调用一次)
initialize是类第一次接收到消息时候调用,每个类只会initialize一次(分类的initialize可能会被调用多次)
3.调用顺序
load:
1.先调用类的load(先编译先调用,调用子类的load之前会先调用父类的load)
2.再调用分类的load(先编译先调用)
initialize:先初始化父类,再初始化子类(可能最终调用的是父类的initialize方法)