我们都知道,App启动时,代码中+(void)load方法早于main函数调用,main函数是我们程序的入口,那么我们程序在main函数之前,都做了些什么工作呢?今天我们一起去探讨一下main函数之前的流程!
首先我们的应用程序,会依赖很多的库-----被操作系统写入内存的可执行代码的二进制!
库又有静态库如.a文件和动态库如.framework
- App启动之后,main函数加载之前的过程:
- dyld::_main的流程
- 上图中的两张图片
那么,我们我们接下来看_objc_init()之后的调用流程及类是如何加载到内存的!
- _objc_init(): 引导程序初始化,注册我们的镜像通知与dyld.在库初始化之前被libSystem调用!
void _objc_init(void) { static bool initialized = false; if (initialized) return; initialized = true; // fixme defer initialization until an objc-using image is found? environ_init(); //读取影响运行时的环境变量,如果需要,还可以打印环境变量 tls_init(); //关于线程key的绑定 例如析构函数设置静态密钥key //运行c++静态构造函数,libc在dyld调用静态构造函数之前调用_objc_init(), //所以我们必须自己做. static_init(); lock_init();//空实现 //初始化libobjc的异常处理系统,由map_images()调用 exception_init(); //注册镜像加载通知回调 /** 注:仅供objc运行时使用 映射、未映射和初始化objc映像时要调用的寄存器处理程序 Dyld将使用包含objc-image-info部分的镜像数组调用“mapped”函数 那些是dylib的镜像会自动替换ref计数,因此objc不再需要对它们调用dlopen()来防止它们被卸载 在调用_dyld_objc_notify_register()期间,dyld将调用已经加载了objc镜像的“映射”函数 在以后的dlopen()调用期间,dyld还将调用“mapped”函数 当Dyld在该镜像中被称为初始化器时,Dyld将调用“init”函数 这是objc调用图像中的任何+load方法的时候 */ _dyld_objc_notify_register(&map_images, load_images, unmap_image); }
- map_images分析 我们上面思维导图分析了map_images的流程,大致了解了类的读取,初始化,映射等的操作,下面我们看看load_images对类的加载!
在讲load_images方法之前,我们需要了解一些知识点:
- 懒加载类与非懒加载类
- 在read_images()思维导图中的:对所有类进行重映射步骤中,在源码中有一段注释这么写的: Realize non-lazy classes (for +load methods and static instances).有道翻译:实现非惰性类(用于+load方法和静态实例).我们可以看出,在read_images()中,初始化的都是非懒加载的类,既然有非懒加载的类,那么就应该有懒加载的类,那么非懒加载与懒加载类是怎么区分的呢?懒加载的类又是什么时候初始化的呢?
- 非懒加载类在编译期就进行编译链接实现了,而懒加载类是在我们用到的时候才去调用,初始化!这两种类的区分方法就是:看这个类的实现中是否实现了+load方法!!!
- 如果我们在类中实现了+load方法,那么这个类在编译期就会进行初始化实现,没有实现load方法,那么这个类在read_images()中就不会进行初始化,映射等的操作!
- 那么.懒加载的类的调用时机,我们在这里提一下,后期会讲到这个!
- 我的博客里有两篇是讲runtime的博文,其中我们有一个方法印象深刻:消息查找或转发:lookupimporforward:
//此处就是懒加载类的调用,没有实现的类就会走到这里(消息转发过程调用!!!) //实现这个懒加载的类,非懒加载的类(编译期实现的)是不会走下面的流程的. if (!cls->isRealized()) { cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock); } static Class realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock) { return realizeClassMaybeSwiftMaybeRelock(cls, lock, true); } static Class realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked) { lock.assertLocked(); if (!cls->isSwiftStable_ButAllowLegacyForNow()) {//OC // Non-Swift class. Realize it now with the lock still held. //返回类的实际类结构.就是进行类的初始化操作!!! realizeClassWithoutSwift(cls); if (!leaveLocked) lock.unlock(); } else {//swift // Swift class. We need to drop locks and call the Swift // runtime to initialize it. lock.unlock(); cls = realizeSwiftClass(cls); assert(cls->isRealized()); // callback must have provoked realization if (leaveLocked) lock.lock(); } return cls; }
- 所以.下面的load_images()也是对已经加入到类列表的类进行load操作!(不包含懒加载的类!)
- load_images分析:
void load_images(const char *path __unused, const struct mach_header *mh) { //hasLoadMethods判断是否实现了+load方法,没有直接返回 //也就是说,我们加载的是实现了load方法的类(系统的一些类我们不去管) // 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 //找到实现了+load方法的类并加入到一个表,方便操作,load方法调用完成,这个表会被释放. { mutex_locker_t lock2(runtimeLock); prepare_load_methods((const headerType *)mh); } // Call +load methods (without runtimeLock - re-entrant) //调用 call_load_methods(); }
上面我们经过分析,知道load方法的调用时机以及是如何调用的!
经过上面的流程,非懒加载类及分类的加载完成,以及load方法的调用时机以及是如何调用的,_objc_init()方法至此调用完成!下一篇我们会谈谈类的拓展、类拓展与分类的区别、类与分类的关系,以及分类属性关联、initialize方法调用时机等内容!
谢谢观赏!