美文网首页iOS开发技巧
OC底层原理11—dyld与objc的关联

OC底层原理11—dyld与objc的关联

作者: 夏天的枫_ | 来源:发表于2020-10-16 01:23 被阅读0次

    本文介绍dyld与objc的关联

    按照苹果的解释,dyld加载库时,libSystem会调用_objc_init,用dyld注册镜像程序。那来看看Objc中的初始化_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?
        //读取影响运行时的环境变量,如果需要,还可以打开环境变量帮助 export OBJC_HRLP = 1
        environ_init();
        //关于线程key的绑定,例如线程数据的析构函数
        tls_init();
        //运行C++静态构造函数,在dyld调用我们的静态析构函数之前,libc会调用_objc_init(),因此我们必须自己做
        static_init();
        //runtime运行时环境初始化,里面主要是unattachedCategories、allocatedClasses -- 分类初始化
        runtime_init();
        //初始化libobjc的异常处理系统
        exception_init();
        //缓存条件初始化
        cache_init();
        //启动回调机制,通常这不会做什么,因为所有的初始化都是惰性的,但是对于某些进程,我们会迫不及待地加载trampolines dylib
        _imp_implementationWithBlock_init();
    
        /*
         _dyld_objc_notify_register -- dyld 注册的地方
         - 仅供objc运行时使用
         - 注册处理程序,以便在映射、取消映射 和初始化objc镜像文件时使用,dyld将使用包含objc_image_info的镜像文件数组,回调 mapped 函数
         map_images: dyld将image镜像文件加载进内存时,会触发该函数
         load_images:dyld初始化image会触发该函数
         unmap_image:dyld将image移除时会触发该函数
         */
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    
    #if __OBJC2__
        didCallDyldNotifyRegister = true;
    #endif
    }
    
    • environ_init(); 环境变量的设置,可以更好的监控程序。小到去除打印日志时的时间,还可以设置调用函数,检验哪些类是否调用了固定的函数(诸如load,initwithxxx等)就可以在
      Edit Scheme
      添加OBJC_PRINT_LOAD_METHODS 为YES,就可以打印出实现load方法的消息。

    dyld_objc_notify_register

    这是dyld注册处理程序

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    

    dyld源码中找到该方法的解释

    //
    // Note: only for use by objc runtime
    // Register handlers to be called when objc images are mapped, unmapped, and initialized.
    // Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section.
    // Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to
    // call dlopen() on them to keep them from being unloaded.  During the call to _dyld_objc_notify_register(),
    // dyld will call the "mapped" function with already loaded objc images.  During any later dlopen() call,
    // dyld will also call the "mapped" function.  Dyld will call the "init" function when dyld would be called
    // initializers in that image.  This is when objc calls any +load methods in that image.
    //
    void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                    _dyld_objc_notify_init      init,
                                    _dyld_objc_notify_unmapped  unmapped);
    {
      dyld::registerObjCNotifiers(mapped, init, unmapped);
    }
    
    

    注意:仅供objc运行时使用
    在objc镜像映射、未映射和初始化时调用,用来注册处理程序。
    dyld将会通过一个包含objc-image-info的镜像文件的数组回调mapped函数。
    那些是dylibs的镜像将自动增加引用计数,因此objc以防止它们被卸载,将不再需要
    对它们调用dlopen()。 在调用_dyld_objc_notify_register()的过程中,dyld将使用已加载的objc镜像调用“mapped”函数。 在以后的任何dlopen()调用期间,dyld也将调用“mapped”函数。
    当调用dyld时,dyld将调用“ init”函数初始化镜像。
    objc镜像在调用任何类方法+load()的时候,也会初始化。

    方法中的三个参数分别表示的含义如下:
    map_images:dyld将image(镜像文件)加载进内存时,会触发该函数
    load_image:dyld初始化image会触发该函数
    unmap_image:dyld将image移除时,会触发该函数


    image.png
    • 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());
            }
        }
    }
    

    在objc中调用初始化时objc_init,会调用 _dyld_objc_notify_register(&map_images, load_images, unmap_image);,在dyld中其实是调用

    void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                    _dyld_objc_notify_init      init,
                                    _dyld_objc_notify_unmapped  unmapped);
    {
      dyld::registerObjCNotifiers(mapped, init, unmapped);
    }
    

    分析registerObjCNotifiers实现,得到了
    map_images = sNotifyObjCMapped = mapped
    load_images = sNotifyObjCInit = init
    unmap_image = sNotifyObjCUnmapped = unmapped

    相关文章

      网友评论

        本文标题:OC底层原理11—dyld与objc的关联

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