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方法
中。
流程: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文件中引用其他库的地方进行引用,生成最后的可执行文件
4.动态链接器dyld
dyld(the dynamic link editor)
是苹果的动态链接器
,是苹果操作系统的重要组成部分,在app
被编译打包成可执行文件格式的Mach-O文件
后,交由dyld
负责连接,并加载程序。
dyld(the dynamic link editor)
是苹果的动态链接器
,是苹果操作系统的重要组成部分,在app
被编译打包成可执行文件格式的Mach-O文件
后,交由dyld
负责连接,并加载程序。
dyld的作用:
加载各个库,也就是image镜像文件
,由dyld
从内存中读到表中,加载主程序,link
链接各个动静态库,进行主程序初始化。
3.dyld入口探索
如何寻找入口?没错,bt
!!! 在Viewcontroller
的load方法
添加断点,运行程序。在控制台输入指令bt
,查看运行的函数调用堆栈信息。见下图: