• 0

  • 483

iOS - 类的加载一(dyld和objc的关联)

1星期前

一.首先探索dyld加载流程

我们初步窥探了dyld的加载流程:

_dyld_start -> dyldbootstrap -> dyld::_main,在dyld::_main流程的最后一步寻找主程序入口,我们进入了recursiveInitialization方法,首先调用了context.notifySingle方法进行单个通知注入,调用回调函数load_images,调用doInitialization方法的时候,会从dyld -> libSystem -> libdispatch -> objc中的_objc_init_objc_init又调用了dyld中实现的_dyld_objc_notify_register注册回调函数,这样就构成了跨库闭环。

二、_objc_init探索

1、environ_init方法:环境变量初始化

environ_init方法的源码如下,其中的关键代码是 for 循环

void environ_init(void) 
{
    //...省略部分逻辑
    if (PrintHelp  ||  PrintOptions) {
        //...省略部分逻辑
        for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
            const option_t *opt = &Settings[i];            
            if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
            if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
        }
    }
}
复制代码

有以下两种方式可以打印所有的环境变量

  • 1、如图。环境变量的设置 Product -> Scheme -> Edit Scheme, 以OBJC_PRINT_LOAD_METHODS为例设置成YES,打印所有实现了load方法的类

    2、通过终端命令export OBJC_hrlp = 1,打印环境变量 所以,OBJC_PRINT_LOAD_METHODS可以监控所有的+load方法,从而处理启动优化(后续会总结下启动优化方法)

2、tls_init:线程key的绑定

主要是本地线程池的初始化以及析构,源码如下

3、static_init:运行系统级别的C++静态构造函数

主要是运行系统级别的C++静态构造函数,在dyld调用我们的静态构造函数之前,libc调用_objc_init方法,即系统级别的C++构造函数 先于 自定义的C++构造函数 运行

4、runtime_init:运行时环境初始化

主要是运行时的初始化,主要分为两部分:分类初始化类的表初始化(后续会详细讲解对应的函数)

5、exception_init:初始化libobjc的异常处理系统

初始化libobjc的异常处理系统,注册异常处理的回调,从而监控异常的处理,源码如下

6、cache_init:缓存初始化

7、_imp_implementationWithBlock_init:启动回调机制

该方法主要是启动回调机制,通常这不会做什么,因为所有的初始化都是惰性的,但是对于某些进程,我们会迫不及待地加载libobjc-trampolines.dylib

7、_dyld_objc_notify_register:dyld注册,垮库调用

方法中的三个参数分别表示的含义如下:

map_images:dyld将image(镜像文件)加载进内存时,会触发该函数

load_image:dyld初始化image会触发该函数

unmap_image:dyld将image移除时,会触发该函数

以上引发了dyldObjc的关联

===> libobjc源码中--调用 _dyld_objc_notify_register(&map_images, load_images, unmap_image);

1、mapped ===(等价于) map_images 2、init === 等价于 load_images 3、unmapped 等价于 unmap_image 注意:map_images前面有&,属于引用类型,外界条件变,它就会跟着变,要传递值 注意:load_images属于值类型,不传递值

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());
        }
    }
}
复制代码

objc把回调函数map_images、load_images、unmap_image分别传给了dyld的sNotifyObjCMapped、sNotifyObjCInit、sNotifyObjCUnmapped 全局搜索sNotifyObjCMapped,发现在notifyBatchPartial方法中调用了sNotifyObjCMapped

static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
{
...
//: -- sNotifyObjCMapped 调用
(*sNotifyObjCMapped)(objcImageCount, paths, mhs);
...
}
复制代码

在上面的registerObjCNotifiers方法中就对notifyBatchPartial进行了调用,即objc中调用_dyld_objc_notify_register进入dyld后,就会回调map_images,进行镜像文件的映射.这样就构成了跨库闭环,就把objcdyld进行了关联。

免责声明:文章版权归原作者所有,其内容与观点不代表Unitimes立场,亦不构成任何投资意见或建议。

ios

483

相关文章推荐

未登录头像

暂无评论