iOS 底层原理-dyld应用程序加载

46 阅读3分钟

1.案例

引入一个案例,在ViewController.m中添加一个load方法,程序main.m中添加一个C++函数。 ViewControler代码如下:

@interface ViewController ()

@end

@implementation ViewController

+ (void)load{

    NSLog(@"%s",__func__);

}

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view.

}

@end

main.m代码如下:

__attribute__((constructor)) void JHSFunc(){

    printf("来了 : %s \n",__func__);

}
int main(int argc, char * argv[]) {

    NSString * appDelegateClassName;
    NSLog(@"1223333");
    @autoreleasepool {

        // Setup code that might create autoreleased objects goes here.

        appDelegateClassName = NSStringFromClass([AppDelegate class]);

    }

    return UIApplicationMain(argc, argv, nil, appDelegateClassName);

}

运行程序,根据运行日志,发现程序首先运行了load方法,然后调用了C++函数,最后才进入到main方法中。

1.png

流程:load -> C++ -> main。为什么?main函数难道不是应用程序的入口吗?在main之前到底做了什么?我们一步步来探究。

2.应用程序编译过程

1.静态库

在链接阶段,会将汇编生成的目标程序与引用的库一起链接打包到可执行文件当中。此时的静态库就不会在改变了,因为它是编译时被直接拷贝一份,复制到目标程序里的。例如:.a.lib

  • 优点:编译完成后,库文件实际上就没有作用了,目标程序没有外部依赖,直接就可以运行
  • 缺点:由于静态库可能会有两份,所以会导致目标程序的体积增大,对内存、性能、速度消耗很大

2.动态库

程序编译时并不会链接到目标程序中,目标程序只会存储指向动态库的引用,在程序运行时才被载入。例如:.so.framwork.dll

  • 优点:减少打包之后app的大小,共享内存,节约资源,更新动态库,达到更新程序
  • 缺点:动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境,如果环境缺少了动态库,或者库的版本不正确,就会导致程序无法运行

3.编译过程

  • .h、.m、.cpp等源文件->预编译->编译->汇编->链接(动静态库加持)->可执行文件。
  • 源文件:载入.h、.m、.cpp等文件
  • 预处理:替换宏,删除注释,展开头文件,产生.i文件
  • 编译:将.i文件转换为汇编语言,产生.s文件
  • 汇编:将汇编文件转换为机器码文件,产生.o文件
  • 链接:对.o文件中引用其他库的地方进行引用,生成最后的可执行文件

3.png

4.动态链接器dyld

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统的重要组成部分,在app被编译打包成可执行文件格式的Mach-O文件后,交由dyld负责连接,并加载程序。

dyld(the dynamic link editor)是苹果的动态链接器,是苹果操作系统的重要组成部分,在app被编译打包成可执行文件格式的Mach-O文件后,交由dyld负责连接,并加载程序。

  • dyld的作用:加载各个库,也就是image镜像文件,由dyld从内存中读到表中,加载主程序, link链接各个动静态库,进行主程序初始化。

4.png

3.dyld入口探索

如何寻找入口?没错,bt!!! 在Viewcontrollerload方法添加断点,运行程序。在控制台输入指令bt,查看运行的函数调用堆栈信息。见下图:

5.png

:下载dyld源码这部分源码是不能编译的。 最新dyld

源码崩溃后续补充