美文网首页
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