iOS 底层学习13

iOS 底层第13天的学习。在第12天的学习中,已经梳理了 dyld 整个的流程。但是在registerObjCNotifiers调用三个参数时候,是何时进行调用和执行的还不清楚,接下来具体分析。

registerObjCNotifiers

  • libobjc:_objc_init 调用 _dyld_objc_notify_register 会传入 3 个参数
void _objc_init(void)
{
   //...  省略 一些 init  的方法
    _imp_implementationWithBlock_init();
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}
  • 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;
}
libojc dyld
&map_images sNotifyObjCMapped
load_images sNotifyObjCInit
unmap_image sNotifyObjCUnmapped
  • 我们得知在 registerObjCNotifiers3个参数进行了赋值,而何时调用还不清楚
    接下来我们要对这几个参数如何调用来进行分析

sNotifyObjCMapped

  • 全局搜索 sNotifyObjCMapped
static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification)
{
 // ... 省略部分代码
 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);
   }
               
 //  .... 
  • notifyBatchPartial进行了 sNotifyObjCMapped 调用,全局搜索 sNotifyObjCMapped
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
    }
}
  • 发现在 registerObjCNotifiers 里就直接调用了 notifyBatchPartial

sNotifyObjCInit

  • 全局搜索 sNotifyObjCInit
static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo)
{
       // ... 省略部分代码
    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);
        }
    }
  • 发现在 notifySingle 进行了调用,全局搜索 notifySingle ,看看那在何时调用了 notifySingle
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
                                          InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
    // let objc know we are about to initialize this image
    uint64_t t1 = mach_absolute_time();
    fState = dyld_image_state_dependents_initialized;
    oldState = fState;
     // 在这里调用了 context.notifySingle
    context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
    
    // initialize this image
    bool hasInitializers = this->doInitialization(context);

    // let anyone know we finished initializing this image
    fState = dyld_image_state_initialized;
    oldState = fState;
    context.notifySingle(dyld_image_state_initialized, this, NULL);

}
  • 发现在 recursiveInitialization 进行了 notifySingle 的调用

但这时有个疑问就是 notifySingle为何在 doInitialization(初始化所有images的赋值) 之前进行调用呢,不是应该在赋值完 之后再进行 notifySingle 调用嘛?

  • 因为 recursiveInitialization 这是一个递归调用,当第一次进来的时候是 libsystem 没有依赖其他底层文件 ,直接会进入 doInitialization 进行初始化,初始化完毕后再调用 context.notifySingle

sNotifyObjCUnmapped

  • 全局搜索 sNotifyObjCUnmapped
void removeImage(ImageLoader* image) {
    // ... 省略部分代码
    if ( sNotifyObjCUnmapped !=  NULL && image->notifyObjC() )
            (*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader());

}
  • removeImage 时调用了 sNotifyObjCUnmapped,继续搜索 removeImage
// This function is called at the end of dlclose() when the reference count goes to zero.
// The dylib being unloaded may have brought in other dependent dylibs when it was loaded.
// Those dependent dylibs need to be unloaded, but only if they are not referenced by
// something else.  We use a standard mark and sweep garbage collection.
//
void garbageCollectImages() 
{
        do {
        sRedo = false;
         // ... 省略部分代码
        // collect phase: delete all images which are not marked in-use
        bool mightBeMore;
        do {
            mightBeMore = false;
            for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
                ImageLoader* image = *it;
                if ( ! image->isMarkedInUse() ) {
                    try {
                        if (gLogAPIs) dyld::log("dlclose(), deleting %p %s\n", image, image->getShortName());
                        removeImage(image);
                        ImageLoader::deleteImage(image);
                        mightBeMore = true;
                        break;  // interator in invalidated by this removal
                    }
                    catch (const char* msg) {
                        dyld::warn("problem deleting image: %s\n", msg);
                    }
                }
            }
        } while ( mightBeMore );
    } while (sRedo);

}
  • 发现 garbageCollectImages 调用了 removeImage,根据注解当 dyld 没有加载到dependent dylibs 就进行此方法的调用

C++ 函数在何时进行调用

  • 新建一个工程,代码👇
  • main
int main(int argc, char * argv[]) {
    NSLog(@">>>> 调用了 : %s \n",__func__);
}
  • c++
__attribute__((constructor)) void cxxFunc(){
    printf(">>>> 调用了 : %s \n",__func__);
}
  • load
+ (void) load {
    NSLog(@">>>> 调用了 : %s \n",__func__);
}
  • 运行后如下
  • 可知在load 后加载了 cxxFunc,那在何时调用的呢?
  • 下个断点
  • bt 打印堆栈
  • 发现在 ImageLoaderMachO::doModInitFunctions 调用了 c++ 方法

流程总结

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容