美文网首页
11、dyld与objC的关联

11、dyld与objC的关联

作者: ChenL | 来源:发表于2020-10-14 15:37 被阅读0次

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源码中找到其调用的地方,他们之间的真正关联关系

相关文章

网友评论

      本文标题:11、dyld与objC的关联

      本文链接:https://www.haomeiwen.com/subject/cwtwpktx.html