一、初探
-
load
函数地址调用!!! 不经过消息查找流程,也就是发送消息(objc_msgSend:) -
initialize
当前对象接收到第一条消息的时候会调用!!!
//当前对象接收到第一条消息的时候会调用!!!
+ (void)initialize {
NSLog(@"%@,%s",self,__func__);
}
//函数地址调用!!! 不经过消息查找流程,也就是发送消息(objc_msgSend:)
+ (void)load{
// NSLog(@"load");//不会执行initialize
NSLog(@"%@,%s",self,__func__);//当前 self 接受到了消息,会执行initialize
}
![](https://img.haomeiwen.com/i3572360/2e3052ae2ead7329.png)
二、initialize源码分析
1、objc4-750源码中搜索lookUpImpOrForward
extern IMP lookUpImpOrForward(Class, SEL, id obj, bool initialize, bool cache, bool resolver);
2、点击lookUpImpOrForward
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// Optimistic cache lookup
// 查找缓存!!!
if (cache) {
//汇编代码的方式实现的!
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
// runtimeLock is held during isRealized and isInitialized checking
// to prevent races against concurrent realization.
// runtimeLock is held during method search to make
// method-lookup + cache-fill atomic with respect to method addition.
// Otherwise, a category could be added but ignored indefinitely because
// the cache was re-filled with the old value after the cache flush on
// behalf of the category.
runtimeLock.lock();
checkIsKnownClass(cls);
if (!cls->isRealized()) {
realizeClass(cls);
}
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
}
retry:
runtimeLock.assertLocked();
// Try this class's cache.
imp = cache_getImp(cls, sel);
if (imp) goto done;
// Try this class's method lists.
{
//Method(SEL IMP)
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
// Try superclass caches and method lists.
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;
}
}
// Superclass method list.
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// No implementation found. Try method resolver once.
if (resolver && !triedResolver) {
runtimeLock.unlock();
_class_resolveMethod(cls, sel, inst);
runtimeLock.lock();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// No implementation found, and method resolver didn't help.
// Use forwarding.
//_objc_msgForward
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlock();
return imp;
}
3、点击_class_initialize
- 递归调用,先调用父类,再调用子类
void _class_initialize(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()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
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;
}
// Send the +initialize message.
// Note that +initialize is sent to the superclass (again) if
// this class doesn't implement +initialize. 2157218
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
//
// Only __OBJC2__ adds these handlers. !__OBJC2__ has a
// bootstrapping problem of this versus CF's call to
// objc_exception_set_functions().
#if __OBJC2__
@try
#endif
{
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
pthread_self(), cls->nameForLogging());
}
}
#if __OBJC2__
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: thread %p: +[%s initialize] "
"threw an exception",
pthread_self(), cls->nameForLogging());
}
@throw;
}
@finally
#endif
{
// Done initializing.
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (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);
}
}
else if (cls->isInitialized()) {
// Set CLS_INITIALIZING failed because someone else already
// initialized the class. Continue normally.
// NOTE this check must come AFTER the ISINITIALIZING case.
// Otherwise: Another thread is initializing this class. ISINITIALIZED
// is false. Skip this clause. Then the other thread finishes
// initialization and sets INITIALIZING=no and INITIALIZED=yes.
// Skip the ISINITIALIZING clause. Die horribly.
return;
}
else {
// We shouldn't be here.
_objc_fatal("thread-safe class init in objc runtime is buggy!");
}
}
4、点击callInitialize
- 消息查找流程
void callInitialize(Class cls)
{
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
asm("");
}
三、initialize总结
- 调用时机:收到第一条消息
- 调用顺序:先父类、再子类
- 调用次数:多次(父类实现了
initialize
,子类没实现,初始化子类,父类会调用两次),load
方法只会调用一次
父类实现了initialize,子类没实现
2019-09-09 10:46:58.553175+0800 LGTest[9621:1967424] LGPerson,+[LGPerson initialize]
2019-09-09 10:46:58.553226+0800 LGTest[9621:1967424] LGStudent,+[LGPerson initialize]
父类实现了initialize,子类也实现了initialize
2019-09-09 10:47:25.426857+0800 LGTest[9638:1969466] LGPerson,+[LGPerson initialize]
2019-09-09 10:47:25.426949+0800 LGTest[9638:1969466] LGStudent,+[LGStudent initialize]
- 分类中的实现,只执行分类的调用(分类的头文件都不需要导入)
- 作用:初始化常量
总结:
情况一:父类实现initialize
1、调用父类,执行父类的initialize
2、调用子类,执行父类的initialize
情况二:子类实现initialize
1、调用父类,不执行
2、调用子类,执行子类的initialize
情况三:分类实现initialize
1、调用父类,执行分类的initialize
2、调用子类,执行分类的initialize
情况四:父类实现initialize
、分类实现initialize
1、调用父类,执行分类的initialize
2、调用子类,执行分类的initialize
情况五:父类实现initialize
、子类实现initialize
1、调用父类,执行父类的initialize
2、调用子类,执行父类的initialize
、执行子类的initialize
情况六:分类实现initialize
、子类实现initialize
1、调用父类,执行分类的initialize
2、调用子类,执行分类的initialize
、执行子类的initialize
情况七:父类实现initialize
、分类实现initialize
、子类实现initialize
1、调用父类,执行分类的initialize
2、调用子类,执行分类的initialize
、执行子类的initialize
分类的作用相当于覆盖类,就是如果分类和类都实现了某个方法,会调用分类的。
网友评论