一、预热
应用程序加载过程会依赖很多的底层库,而底层库不应该每次都从0加载。
库是一种二进制的可执行代码。
- 静态库
- .a
- .lib
- 动态库:
- .so
- .framework
- .dll
静态链接:在链接阶段会将汇编生成的目标文件.o与引用的(静态)库一起链接打包生成可执行文件的过程
动态链接:动态库在编译期间不会加载,在运行程序的时候再加载,共享内存,会被多次使用
编译过程
- 源文件 .h .m .cpp
- 预编译
- 编译
- 汇编
- 链接 - .a .lib .so
- 可执行文件
1、load方法在什么时候调用
2、子类和父类以及分类 load方法调用顺序是什么
3、子类和父类以及分类initialize方法调用顺序是什么
二、app的加载过程
1、app启动
-
dyld动态链接器 - 系统库通过dyld来管理 - 赋予静态库的运行时功能
-
加载libSystem
-
通过runtime注册回调函数
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(); static_init(); lock_init(); exception_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image); // 这个 } // ----------------------------------------------------- // Note: only for use by objc runtime // Register handlers to be called when objc images are mapped, unmapped, and initialized. // Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section. // Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to // call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(), // dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call, // dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called // initializers in that image. This is when objc calls any +load methods in that image. // void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped);_dyld_objc_notify_mapped mapped加载镜像文件,返回成功或失败,成为参数- initializers in that image. This is when objc calls any +load methods in that image.
-
加载新image
-
执行map_images、load_images ------imageLoader加载image-----> 返回 3. 循环直到加载完毕
-
调用main函数
-
2、map_images
-
经过一些处理后进入
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses)-
加载类 --- gdb_objc_realized --- class
-
initializeTaggedPointerObfuscator(); -
创造一张哈希表
-
gdb_objc_realized_classes = NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize); // ----------- // NX 是一种cpu的技术,分为rw和ro -
// Discover classes. Fix up unresolved future classes. Mark bundle classes.发现有类,加载类有两种情况:1是已经加载过了,2是没有加载过,说明不是懒加载
-
已经加载过了:
有重映射过程
for (EACH_HEADER) { classref_t *classlist = _getObjc2ClassList(hi, &count); if (! mustReadClasses(hi)) { // Image is sufficiently optimized that we need not call readClass() continue; } bool headerIsBundle = hi->isBundle(); bool headerIsPreoptimized = hi->isPreoptimized(); for (i = 0; i < count; i++) { // ↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓-↓ Class cls = (Class)classlist[i]; Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized); // ↑-↑-↑-↑-↑-↑-↑-↑-↑-↑-↑-↑-↑-↑-↑-↑-↑ if (newCls != cls && newCls) { // Class was moved but not deleted. Currently this occurs // only when the new class resolved a future class. // Non-lazily realize the class below. //**不是懒加载会走这** resolvedFutureClasses = (Class *) realloc(resolvedFutureClasses, (resolvedFutureClassCount+1) * sizeof(Class)); resolvedFutureClasses[resolvedFutureClassCount++] = newCls; } } }Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) { const char *mangledName = cls->mangledName(); // 读取cls的name if (missingWeakSuperclass(cls)) // 进行各种判断,判断该类是否值得加进来 { // No superclass (probably weak-linked). // Disavow any knowledge of this subclass. if (PrintConnecting) { _objc_inform("CLASS: IGNORING class '%s' with " "missing weak-linked superclass", cls->nameForLogging()); } addRemappedClass(cls, nil); cls->superclass = nil; return nil; } // Note: Class __ARCLite__'s hack does not go through here. // Class structure fixups that apply to it also need to be // performed in non-lazy realization below. // These fields should be set to zero because of the // binding of _objc_empty_vtable, but OS X 10.8's dyld // does not bind shared cache absolute symbols as expected. // This (and the __ARCLite__ hack below) can be removed // once the simulator drops 10.8 support. #if TARGET_OS_SIMULATOR if (cls->cache._mask) cls->cache._mask = 0; if (cls->cache._occupied) cls->cache._occupied = 0; if (cls->ISA()->cache._mask) cls->ISA()->cache._mask = 0; if (cls->ISA()->cache._occupied) cls->ISA()->cache._occupied = 0; #endif Class replacing = nil; if (Class newCls = popFutureNamedClass(mangledName)) { // This name was previously allocated as a future class. // Copy objc_class to future class's struct. // Preserve future's rw data block. if (newCls->isAnySwift()) { _objc_fatal("Can't complete future class request for '%s' " "because the real class is too big.", cls->nameForLogging()); } // ↓-重-↓-点-↓-重-↓-点-↓-重-↓-点-↓ class_rw_t *rw = newCls->data(); const class_ro_t *old_ro = rw->ro; memcpy(newCls, cls, sizeof(objc_class)); rw->ro = (class_ro_t *)newCls->data(); newCls->setData(rw); freeIfMutable((char *)old_ro->name); free((void *)old_ro); addRemappedClass(cls, newCls); replacing = cls; cls = newCls; // ↑-重-↑-点-↑-重-↑-点-↑-重-↑-点-↑ } if (headerIsPreoptimized && !replacing) { // class list built in shared cache // fixme strict assert doesn't work because of duplicates // assert(cls == getClass(name)); assert(getClass(mangledName)); } else { // ↓-重-↓-点-↓-重-↓-点-↓-重-↓-点-↓ addNamedClass(cls, mangledName, replacing); addClassTableEntry(cls); // ↑-重-↑-点-↑-重-↑-点-↑-重-↑-点-↑ } // for future reference: shared cache never contains MH_BUNDLEs if (headerIsBundle) { cls->data()->flags |= RO_FROM_BUNDLE; cls->ISA()->data()->flags |= RO_FROM_BUNDLE; } return cls; }static void addNamedClass(Class cls, const char *name, Class replacing = nil) { runtimeLock.assertLocked(); Class old; if ((old = getClass(name)) && old != replacing) { inform_duplicate(name, old, cls); // getNonMetaClass uses name lookups. Classes not found by name // lookup must be in the secondary meta->nonmeta table. addNonMetaClass(cls); } else { // ↓-重-↓-点-↓-重-↓-点-↓-重-↓-点-↓ NXMapInsert(gdb_objc_realized_classes, name, cls); // 把类加入gdb_objc_realized_classes这个表里 // ↑-重-↑-点-↑-重-↑-点-↑-重-↑-点-↑ } assert(!(cls->data()->flags & RO_META)); // wrong: constructed classes are already realized when they get here // assert(!cls->isRealized()); }-
没有加载:
realizeClass(cls);↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
// Attach categoriesmethodizeClass(cls);
-
-
类加载过一次就会有cache信息
-
-
加载protocol --- protocol_map --- protocol_t
-
加载方法 ----- nameSelectors ---- SEL
// Fix up @selector references static size_t UnfixedSelectors; { mutex_locker_t lock(selLock); for (EACH_HEADER) { if (hi->isPreoptimized()) continue; bool isBundle = hi->isBundle(); SEL *sels = _getObjc2SelectorRefs(hi, &count); UnfixedSelectors += count; for (i = 0; i < count; i++) { const char *name = sel_cname(sels[i]); sels[i] = sel_registerNameNoLock(name, isBundle); } } }NXMapInsert(namedSelectors, sel_getName(result), result);
-
3、load_images
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
{
mutex_locker_t lock2(runtimeLock);
prepare_load_methods((const headerType *)mh); // 进入
// 1、在里面拿到类的列表,然后递归load所有类和父类
/*
load过程中的重点:add_class_to_loadable_list
↓-重-↓-点-↓-重-↓-点-↓-重-↓-点-↓
loadable_classes[loadable_classes_used].cls = cls;
loadable_classes[loadable_classes_used].method = method;
****此method为所有类的load方法****
↑-重-↑-点-↑-重-↑-点-↑-重-↑-点-↑
*/
// 2、在里面拿到所有分类的列表,分类不用递归,因为没有父类
/*
loadable_categories[loadable_categories_used].cat = cat;
loadable_categories[loadable_categories_used].method = method;
*/
}
// Call +load methods (without runtimeLock - re-entrant)
call_load_methods();// 调用所有的load方法
}
do {
// 1. Repeatedly call class +loads until there aren't any more
while (loadable_classes_used > 0) {
call_class_loads();
}
// 2. Call category +loads ONCE
more_categories = call_category_loads();
// 3. Run more +loads if there are classes OR more untried categories
} while (loadable_classes_used > 0 || more_categories);
// 先类,再分类
// 因为添加类的时候递归添加父类,所以先调用load的时候先调用父类的load方法再调用子类的load方法
4、initialize
-
先调用父类在调用子类,在main函数之后
main -> _objc_msgSend_uncached -> _class_lookupMedthodAndLoadCache3 -> lookUpImpForward -> _class_initiallize
其中有一个父类的递归
-
如果分类实现initialize,则原类不会调用initialize,先调用父类的分类,再调用子类
问:是否覆盖?