类的加载(上)
Posted WeaterMr
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类的加载(上)相关的知识,希望对你有一定的参考价值。
类的加载(上)
_objc_init 函数
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();
runtime_init();
exception_init();
#if __OBJC2__
cache_t::init();
#endif
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
// map_images()
// load_images()
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
-
environ_init()
: 读取影响运行时的环境变量。如果需要,还可以打印环境变量帮助。 -
tls_init()
关于线程key
的绑定 - 比如每线程数据的析构函数 -
static_init()
运行C ++
静态构造函数。在dyld
调用我们的静态构造函数之前,libc
会调用_objc_init()
, 因此我们必须自己做 -
lock_init()
: 没有重写,采用C++ 的特性 -
exception_init ()
初始化libobjc的异常处理系统 -
cache_init()
缓存条件初始化 -
runtime_init()
: runtime运行时环境初始化,里面主要
是:unattachedCategories,allocatedClasses 后面会分析 -
_imp_implementationWithBlock_init :
启动回调机制。通常这不会做什么,因为所有的初始化都
是惰性的,但是对于某些进程,我们会迫不及待地加载trampolines dylib。
环境变量的获取与作用
for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++)
const option_t *opt = &Settings[i];
_objc_inform("%s: %s", opt->env, opt->help);
_objc_inform("%s is set", opt->env);
bjc[1835]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[1835]: OBJC_PRINT_IMAGES is set
objc[1835]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[1835]: OBJC_PRINT_IMAGE_TIMES is set
objc[1835]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[1835]: OBJC_PRINT_LOAD_METHODS is set
objc[1835]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[1835]: OBJC_PRINT_INITIALIZE_METHODS is set
objc[1835]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[1835]: OBJC_PRINT_RESOLVED_METHODS is set
objc[1835]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[1835]: OBJC_PRINT_CLASS_SETUP is set
objc[1835]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[1835]: OBJC_PRINT_PROTOCOL_SETUP is set
objc[1835]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[1835]: OBJC_PRINT_IVAR_SETUP is set
objc[1835]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
我们可以通过一些环境变量做一些特殊处理。
上图第一个环境变量是获取干净的类信息。
上图第二个环境变量是打印所有的 + load 方法,有利于我们做程序启动优化。
_dyld_objc_notify_register
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
该函数的两个参数,
1.&map_images
表示指针传递,和内部调用的函数保持同步。&map_images
主要是映射镜像文件,耗时操作。管理文件中和动态库中所有的符号(class Protocol selector category
)
2.load_image
加载执行load方法
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
if (hCount > 0)
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
_read_images函数
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
header_info *hi;
uint32_t hIndex;
size_t count;
size_t i;
Class *resolvedFutureClasses = nil;
size_t resolvedFutureClassCount = 0;
static bool doneOnce;
bool launchTime = NO;
TimeLogger ts(PrintImageTimes);
runtimeLock.assertLocked();
ts.log("IMAGE TIMES: first time tasks");
。。。
ts.log("IMAGE TIMES: fix up selector references");
。。。
ts.log("IMAGE TIMES: discover classes");
。。。
ts.log("IMAGE TIMES: remap classes");
。。。
ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
。。。
ts.log("IMAGE TIMES: discover protocols");
。。。
ts.log("IMAGE TIMES: fix up @protocol references");
。。。
ts.log("IMAGE TIMES: discover categories");
。。。
ts.log("IMAGE TIMES: realize non-lazy classes");
。。。
ts.log("IMAGE TIMES: realize future classes");
。。。
_objc_inform("PREOPTIMIZATION: %zu selector references not "
"pre-optimized", UnfixedSelectors);
_objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted",
PreoptOptimizedMethodLists,
通过源码分析可以得到以下流程:
• • • • • • • • • •
- 1: 条件控制进行一次的加载
- 2: 修复预编译阶段的
@selector
的混乱问题 - 3: 错误混乱的类处理
- 4:修复重映射一些没有被镜像文件加载进来的类
- 5: 修复一些消息!
- 6: 当我们类里面有协议的时候 : readProtocol 7: 修复没有被加载的协议
8: 分类处理重点
9: 类的加载处理重点
- 10 : 没有被处理的类 优化那些被侵犯的类
static size_t UnfixedSelectors;
mutex_locker_t lock(selLock);
for (EACH_HEADER)
if (hi->hasPreoptimizedSelectors()) 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]);
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel)
sels[i] = sel;
这里的sel 即为名称加地址,sel所在的位置不同进行相关的修复。
核心函数 readClass
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
const char *mangledName = cls->nonlazyMangledName();
我们可以通过判断 mangledName
和我们要研究的类是否相同,然后做特殊的处理。然后将断电断到条件中,就可以看当前我们要研究的类的流程。
未完待续。。。
以上是关于类的加载(上)的主要内容,如果未能解决你的问题,请参考以下文章