美文网首页
你真的了解dyld么?

你真的了解dyld么?

作者: HotPotCat | 来源:发表于2021-04-29 17:28 被阅读0次

    有一个工程DyldDemo依赖AB两个动态库,依赖信息如下图所示:

    image.png

    DyldDemo关键内容

    AppDelegate.m:

    + (void)load {
        NSLog(@"AppDelegate load");
    }
    

    ViewController.m:

    + (void)load {
        NSLog(@"ViewController load");
    }
    

    ViewController+Additions.m:

    + (void)load {
        NSLog(@"ViewController Additions load");
    }
    

    AppDelegate+Additions.m:

    #import "AppDelegate+Additions.h"
    
    __attribute__((constructor)) void func3() {
        printf("\n ---func3--- \n");
    }
    
    
    __attribute__((constructor)) void func4() {
        printf("\n ---func4--- \n");
    }
    
    
    @implementation AppDelegate (Additions)
    
    + (void)load {
        NSLog(@"AppDelegate Additions load");
    }
    
    @end
    

    main.m:

    __attribute__((constructor)) void func1() {
        printf("\n ---func1--- \n");
    }
    
    
    __attribute__((constructor)) void func2() {
        printf("\n ---func2--- \n");
    }
    
    int main(int argc, char * argv[]) {
        NSString * appDelegateClassName;
        NSLog(@"---MAIN---");
        @autoreleasepool {
            // Setup code that might create autoreleased objects goes here.
            appDelegateClassName = NSStringFromClass([AppDelegate class]);
        }
        return UIApplicationMain(argc, argv, nil, appDelegateClassName);
    }
    

    A 关键内容

    Compile Sources:

    image.png

    A1.m:

    #import "A1.h"
    
    __attribute__((constructor)) void func_A1() {
        printf("\n ---func_A1--- \n");
    }
    
    @implementation A1
    
    + (void)load {
        NSLog(@"A1 Load");
    }
    
    @end
    

    A2.m:

    #import "A2.h"
    
    __attribute__((constructor)) void func_A2() {
        printf("\n ---func_A2--- \n");
    }
    
    
    @implementation A2
    
    + (void)load {
        NSLog(@"A2 Load");
    }
    
    @end
    

    A1+Addtions.m:

    #import "A1+Addtions.h"
    
    @implementation A1 (Addtions)
    
    + (void)load {
        NSLog(@"A1 Addtions Load");
    }
    
    @end
    

    B 关键内容

    Compile Sources:

    image.png

    B1.m:

    #import "B1.h"
    
    __attribute__((constructor)) void func_B1() {
        printf("\n ---func_B1--- \n");
    }
    
    @implementation B1
    
    + (void)load {
        NSLog(@"B1 Load");
    }
    
    @end
    

    B2.m:

    #import "B2.h"
    
    __attribute__((constructor)) void func_B2() {
        printf("\n ---func_B2--- \n");
    }
    
    @implementation B2
    
    + (void)load {
        NSLog(@"B2 Load");
    }
    
    @end
    

    问题:程序运行到[ViewController viewDidLoad]控制台输出什么?

    ⚠️在Debug环境下,不考虑编译器优化。并且没有进行二进制重排。






    问题分析:

    1. 首先初始化B,再初始化A,最后初始化主程序DyldDemo

    根据dyld源码可知dyld初始化image list从下标1开始,最后再初始化主程序(下标0)。而image list顺序与Link Binary With Libraries顺序有关。

    image.png
    dyld相关源码如下:
    image.png
    1. B分析
      由于B1.mB2.m前面,所以先执行B1+ load方法。所以B1的输出为:
    B1 Load
    B2 Load
    ---func_B1---
    ---func_B2---
    

    根据dyld的源码可知c++的全局构造函数调用在+load方法之后。
    dyld相关源码(ImageLoader::recursiveInitialization):

    image.png
    1. A分析
      A中的文件顺序A1+AdditionsA2A1。所以A中的输出应该为:
    A2 Load
    A1 Load
    A1 Addtions Load
    ---func_A2---
    ---func_A1---
    

    根据dyldobjc源码可知,分类的+load方法加载是在所有类的+load方法加载后,c++构造函数前调用的。
    objc-loadmethod.mm:

    image.png
    1. DyldDemo分析
      文件顺序为main.mAppDelegate+AdditionsViewController+AdditionsViewControllerAppDelegate。根据AB的分析同理可得DyldDemo的输出为:
    ViewController load
    AppDelegate load
    AppDelegate Additions load
    ViewController Additions load
    ---func1---
    ---func2---
    ---func3---
    ---func4---
    ---MAIN---
    

    +load是在objc中调用的,c++全局构造函数是在dyld中调用的,这两个调用都在main函数之前。

    1. 所以整体输出为
    B1 Load
    B2 Load
    ---func_B1---
    ---func_B2---
    A2 Load
    A1 Load
    A1 Addtions Load
    ---func_A2---
    ---func_A1---
    ViewController load
    AppDelegate load
    AppDelegate Additions load
    ViewController Additions load
    ---func1---
    ---func2---
    ---func3---
    ---func4---
    ---MAIN---
    

    运行代码验证:

    image.png

    总结

    • Dyld初始化image是按Link Binary With Libraries顺序逐个初始化的,从下标1开始,最后再初始化主程序(下标0)。可以理解为是按image进行分组的。
    • image内部是先加载所有类的+ load,再加载分类的+ load,最后加载C++全局构造函数。(类load->分类load->C++构造函数)。+loadobjc中调用的,C++全局构造函数是在dyld中调用的。(在不考虑二进制重排等的优化下,image内部的顺序默认是按Compile Sources中顺序进行的)。
    • main函数是在dyld返回入口函数(main)之后才调用的。

    Demo

    相关文章

      网友评论

          本文标题:你真的了解dyld么?

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