IOS initialize 和 load 学习笔记
oc绝大多数类都继承自根类NSObject
该类有两个初始化方法,一个是initialize
另一个则是load
-
+ (void)load
对于加入运行期系统中的每个Class或者Category,必定会调用此方法,并且只会调用一次。load
方法执行时,runtime系统处于“脆弱状态”。如果load
方法中使用了其他类,那么相关类的load方法必定会得到执行。然而,根据指定的库,却无法判断各个类的加载顺序。所以,在load
方法里使用其他类是不安全的。
#import "ClassA.h"
#import "ClassB.h"
@implementation ClassA
+ (void)load
{
ClassB *obj = [ClassB new];//无法确定ClassB是否已经载入
}
@end
遗憾的是NSObject的源码并没有什么东西
// NSObject.mm
@implementation NSObject
+ (void)load {
}
所有我们按照这前一段代码,来验证load方法的一些行为,我们在ClassB *obj = [ClassB new];
之后打下断点,查看程序的调用栈
可以发现[ClassB load]
之前调用了load_images
和call_load_methods
这两个私有方法。
load_images
方法位于源码objc-runtime-new.mm
文件中
void
load_images(const char *path __unused, const struct mach_header *mh)
{
// (1)如果没有实现+load方法,直接return,
// 所以如果父类实现了load方法,此处return之后也不会被调用
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// (2)递归查找所有已导入的父类和分类的load方法(如果实现了load方法的话)此处代码略长,有兴趣可以深究看看
{
rwlock_writer_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh);
}
// 调用+load 方法
call_load_methods();
}
call_load_methods
方法位于源码 objc-loadmethod.h
文件中
void call_load_methods(void)
{
//.....此处代码省略
void *pool = objc_autoreleasePoolPush();
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. 调用一次category分类中的+loads 方法
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
objc_autoreleasePoolPop(pool);
loading = NO;
}
call_load_methods
中调用了其他方法,就不深究下去了
但是仍然可见:
- 如果类本身没有实现load方法,那么不管父类是否实现了load方法,那么都不会调用(见
load_images
中(1)语句)
- 执行子类load方法之前,会执行所有父类的load方法(见
load_images
中(2)语句) - category中的如果定义了load方法,那么也会调用(见
call_load_methods
中2
语句) - 类中的load方法比分类中的load方法先调用,
call_load_methods
方法定义中 先执行call_class_loads ()
语句再执行call_category_loads ()
因为load在类载入时就会调用,如果写了过多耗时操作,就会导致程序阻塞无响应,但是它适合用在Category中判断类是否已经加载。
-
+ (void)initialize
如果真正想执行类的初始化操作,可以实现initialize
方法,与load
区别在于,initialize
是懒加载的,如果某个类没有被使用那么,initialize
方法永远也不会被调用,也就是说,不必像load
方法那样要执行每一个类的load
方法,程序才能继续执行下去。
- 父类若实现了
initialize
方法,即使子类没有实现,那么也会调用父类的initialize
方法
// SuperClass
#import "SuperClass.h"
@interface SuperClass()
@end
@implementation SuperClass
+ (void)initialize
{
NSLog(@"Super initialize");
}
@end
// SubClass
@interface SubClass()
@end
@implementation SubClass
//+ (void)initialize
//{
// NSLog(@"SubClass initialize");
//}
@end
示例程序
可见即使子类未实现
initialize
方法父类也会调用initialize
方法
接下来让我们关心下源码。残念,NSObject.mm也是一如既往的空空如也
@implementation NSObject
+ (void)load {
}
+ (void)initialize {
}
如法炮制,我们在superclass
处打下断点
我们发现调用了_class_initialize
私有方法,源码略长略长
/***********************************************************************
* class_initialize. Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
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);
// 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: calling +[%s initialize]",
cls->nameForLogging());
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
@try {
callInitialize(cls);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
cls->nameForLogging());
}
}
@catch (...) {
if (PrintInitializing) {
_objc_inform("INITIALIZE: +[%s initialize] threw an exception",
cls->nameForLogging());
}
@throw;
}
@finally {
// Done initializing.
// If the superclass is also done initializing, then update
// the info bits and notify waiting threads.
// If not, update them later. (This can happen if this +initialize
// was itself triggered from inside a superclass +initialize.)
monitor_locker_t lock(classInitLock);
if (!supercls || supercls->isInitialized()) {
_finishInitializing(cls, supercls);
} else {
_finishInitializingAfter(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 {
waitForInitializeToComplete(cls);
return;
}
}
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!");
}
}
有点困了😴😴,有空再来填坑吧