objc4-781官方源码 _objc_init方法
// runtime + 类的信息
void _objc_init(void)
{
static bool initialized = false;
if (initialized) return;
initialized = true;
// fixme defer initialization until an objc-using image is found?
//fixme延迟初始化直到找到使用图像的objc?
environ_init();
tls_init();
static_init();
runtime_init();
exception_init();
cache_init();
_imp_implementationWithBlock_init();
// 什么时候调用? images 镜像文件
// map_images()
// load_images()
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
一、初始化环境变量 environ_init();
点击environ_init()进入
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);
}
读取影响运行时的环境变量。如果需要,可以打印环境变量帮助
控制台打印日志如下:
···
objc[38640]: OBJC_DISABLE_TAG_OBFUSCATION is set
objc[38640]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[38640]: OBJC_DISABLE_NONPOINTER_ISA is set
objc[38640]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
objc[38640]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY is set
在不设置环境变量 OBJC_DISABLE_NONPOINTER_ISA的时候,打印 person的isa信息
image.png然后设置环境变量 OBJC_DISABLE_NONPOINTER_ISA 为 YES, 之后再次打印person的isa 信息
image.png image.png可以得到 最后一位 由 1 变成 0,在isa得层分析中 我们介绍过,最后一位是 nonpointer 位,表示是否对isa指针开启指针优化, 0 表示纯isa 指针,1 表示不止是类对象地址。isa 中包含了类信息、对象的引用计数等
若设置打印所有加载的文件的相关的load方法,设置 OBJC_PRINT_LOAD_METHODS = YES,然后再次打印,控制台日志如下:
截屏2020-10-1414.25.40.png二、tls_init(), 关于线程key的绑定,例如:每线程数据的析构函数
void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
三、static_init()
运行C++静态构造函数
在dyld调用我们的静态构造函数之前,libc会调用_objc_init(),因此我们可以自己做一些
四、runtime_init() ,runtime运行时环境初始化
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init();
}
五、exception_init(),异常信息的初始化
我们可以通过设置回调函数来拦截异常信息
六、cache_init();缓存条件初始化
void cache_init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
mach_msg_type_number_t count = 0;
kern_return_t kr;
while (objc_restartableRanges[count].location) {
count++;
}
kr = task_restartable_ranges_register(mach_task_self(),
objc_restartableRanges, count);
if (kr == KERN_SUCCESS) return;
_objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}
七、_imp_implementationWithBlock_init() ,启动回调机制
通常这个不会做什么,因为所有的初始化都是惰性的,但对于某些进程,我们会迫不及待的加载 Trampolines.Initialize();
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
// Eagerly load libobjc-trampolines.dylib in certain processes. Some
// programs (most notably QtWebEngineProcess used by older versions of
// embedded Chromium) enable a highly restrictive sandbox profile which
// blocks access to that dylib. If anything calls
// imp_implementationWithBlock (as AppKit has started doing) then we'll
// crash trying to load it. Loading it here sets it up before the sandbox
// profile is enabled and blocks it.
//
// This fixes EA Origin (rdar://problem/50813789)
// and Steam (rdar://problem/55286131)
if (__progname &&
(strcmp(__progname, "QtWebEngineProcess") == 0 ||
strcmp(__progname, "Steam Helper") == 0)) {
Trampolines.Initialize();
}
#endif
}
八、_dyld_objc_notify_register
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
map_images 是引用类型,load_images 是值类型
_dyld_objc_notify_register 这个方法是跨库执行的,在苹果开源的dyld源码里面可以找到,然后看到调用了 dyld::registerObjCNotifiers 这个方法
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;
sNotifyObjCUnmapped = unmapped;
// call 'mapped' function with all images mapped so far
try {
notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true);
}
catch (const char* msg) {
// ignore request to abort during registration
}
// <rdar://problem/32209809> call 'init' function on all images already init'ed (below libSystem)
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
}
}
}
1、map_images
map_images: 加载镜像文件到内存(将MachO中的类信息加载到内存中)
在 _dyld_objc_notify_register 方法中的参数对应:registerObjCNotifiers 方法中的 sNotifyObjCMapped = mapped; 这一句赋值语句,在dyld源码中,全局搜索一下 sNotifyObjCMapped,同样会发现在 notifyBatchPartial 方法中有sNotifyObjCMapped
if ( objcImageCount != 0 ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_MAP, 0, 0, 0);
uint64_t t0 = mach_absolute_time();
//这句
(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
uint64_t t1 = mach_absolute_time();
ImageLoader::fgTotalObjCSetupTime += (t1-t0);
}
所以说 objC 中_objc_init 方法的调用时离不开dyld的,他们之间有着紧密的联系
探索一下map_images
进入 objc_781 源码,全局搜索相关的函数调用map_images , 我们能进入相关的函数调用过程:
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);
}
再进入到 map_images_nolock 方法中,我们能发现其中有很多加载相关类的信息 _read_images:
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
map_images 这个函数的主要功能就是为了映射相关的类信息,所以此处才是我们研究的重点,接着进入到相关的类方法的定义_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();
......
下节待更新...
2、load_images
我们分析一下 registerObjCNotifiers 方法的第二个参数 init,(也就是_dyld_objc_notify_register 方法中的 load_images 参数)方法里面有sNotifyObjCInit = init; 这个赋值语句,接下来在 dyld 源码中全局搜索一下 sNotifyObjCInit,会发现在notifySingle 方法中 sNotifyObjCInit 的调用:
if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) {
uint64_t t0 = mach_absolute_time();
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
//这句
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());
uint64_t t1 = mach_absolute_time();
uint64_t t2 = mach_absolute_time();
uint64_t timeInObjC = t1-t0;
uint64_t emptyTime = (t2-t1)*100;
if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) {
timingInfo->addTime(image->getShortName(), timeInObjC);
}
}
在objc中_objc_init 方法中调用的 _dyld_objc_notify_register方法在dyld源码中找到其调用的地方,他们之间的真正关联关系
网友评论