Objct-C 中+load和+initialize
2019-08-01 本文已影响38人
zwwuchn
+load
@interfaceLPPerson:NSObject
@end
@implementationLPPerson
+ (void)load {
NSLog(@"LPPerson---+load");
}
@end
@interfaceLPPerson(Test1)
@end
@implementationLPPerson(Test1)
+ (void)load {
NSLog(@"LPPerson (Test1)---+load");
}
@end
@interfaceLPPerson(Test2)
@end
@implementationLPPerson(Test2)
+ (void)load {
NSLog(@"LPPerson (Test2)---+load");
}
@end
intmain(intargc,constchar* argv[]) {
@autoreleasepool{
}
return0;
}
// 打印结果
LPPerson---+load
LPPerson (Test1)---+load
LPPerson (Test2)---+load
从上面代码可以看出:即使我们在我们没有调用LPPerson类、LPPerson分类也会打印。
打印一下类方法
这里我们要使用这个分类DLIntrospection
NSLog(@"%@",[[LPPersonclass] classMethods]);
// 打印结果
(
"+ (void)load",
"+ (void)load",
"+ (void)load"
)
说明类的类方法、类分类的类方法都在元类类方法列表中
下面,我们将结合runtime开源代码一起来揭开它们的神秘面纱。
- objc runtime的加载入口是一个叫_objc_init的方法,在library加载前由libSystem dyld调用,进行初始化操作(在objc-os.mm文件中)
- 调用load_images方法(加载镜像的意思),然后调用call_load_methods()方法。(加载load方法)
void load_images(const char *path __unused, const struct mach_header *mh)
{
// Return without taking locks if there are no +load methods here.
if (!hasLoadMethods((const headerType *)mh)) return;
recursive_mutex_locker_t lock(loadMethodLock);
// Discover load methods
{
rwlock_writer_t lock2(runtimeLock);
// 加载load方法之间做一些准备(所有类的load方法调用顺序)
prepare_load_methods((const headerType *)mh);
}
// 加载load方法
call_load_methods();
}
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertWriting();
classref_t *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
// 定制任务:比如类的加载
schedule_class_load(remapClass(classlist[i]));
}
category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
category_t *cat = categorylist[i];
Class cls = remapClass(cat->cls);
if (!cls) continue; // category for ignored weak-linked class
realizeClass(cls);
assert(cls->ISA()->isRealized());
add_category_to_loadable_list(cat);
}
}
static void schedule_class_load(Class cls)
{
if (!cls) return;
assert(cls->isRealized()); // _read_images should realize
if (cls->data()->flags & RW_LOADED) return;
// 1.先将父类的load方法添加到loadable_list中
schedule_class_load(cls->superclass);
// 2.将cls添加到loadable_list最后面(放到最后面就说明最后调用load方法)
add_class_to_loadable_list(cls);
cls->setInfo(RW_LOADED);
}
void call_load_methods(void)
{
static bool loading = NO;
bool more_categories;
loadMethodLock.assertLocked();
// Re-entrant calls do nothing; the outermost call will finish the job.
if (loading) return;
loading = YES;
void *pool = objc_autoreleasePoolPush();
do {
// 1.首先调用类的load方法
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. 调用Category的load方法
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;
}
首先调用类的load方法
我们查看怎么调用类的load方法call_class_loads()
static void call_class_loads(void)
{
int i;
// 存放所有加载的类数组,loadable_classes: 类数组内部存放类的位置,从而得到多个类之间的调用顺序
struct loadable_class *classes = loadable_classes;
int used = loadable_classes_used;
loadable_classes = nil;
loadable_classes_allocated = 0;
loadable_classes_used = 0;
// Call all +loads for the detached list.
for (i = 0; i < used; i++) {
Class cls = classes[i].cls;
// 直接取出类里面的load方法(load_method:指向类里面load方法的内存地址)
load_method_t load_method = (load_method_t)classes[i].method;
if (!cls) continue;
if (PrintLoading) {
_objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
}
// 找到load指针,直接调用:所以所有的load方法都会调用
(*load_method)(cls, SEL_load);
}
// Destroy the detached list.
if (classes) free(classes);
}
+initialize
+initialize 方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用。也就是说 +initialize 方法是以懒加载的方式被调用的,如果程序一直没有给某个类或它的子类发送消息,那么这个类的 +initialize 方法是永远不会被调用的
我们来看一下runtime源代码
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver)
{
...
rwlock_unlock_write(&runtimeLock);
}
// 是否初始化 并且 这个类没有被初始化
if (initialize && !cls->isInitialized()) {
_class_initialize (_class_getNonMetaClass(cls, inst));
// 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
}
...
}
当我们给某个类发送消息时,runtime 会调用这个函数在类中查找相应方法的实现或进行消息转发
当类没有初始化,会调用_class_getNonMetaClass方法
void _class_initialize(Class cls)
{
...
Class supercls;
BOOL reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// 父类进行递归调用,确保先初始化父类
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
monitor_enter(&classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
monitor_exit(&classInitLock);
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());
}
// runtime 使用了发送消息 objc_msgSend 的方式对 +initialize 方法进行调用
((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
if (PrintInitializing) {
_objc_inform("INITIALIZE: finished +[%s initialize]",
...
}