iOS开发你不知道的事-编译&链接

作者 : 开心源码 本文共3078个字,预计阅读时间需要8分钟 发布时间: 2022-05-12 共250人阅读

对于平时的应用程序开发,我们很少需要关注编译链接过程。我们平时Xcode开发就是集成的的开发环境(IDE),这样的IDE一般都将编译链接的过程一步完成,通常将这种编译链接合并在一起的过程称为构建,即便使用命令行来编译一个源代码文件,简单的一句gcc hello.c命令就包含了非常复杂的过程!

正是由于集成开发环境的强大,很多系统软件的运行机制与机理被掩盖,其程序的很多莫名其妙的错误让我们无所适从,面对程序运行时种种性能瓶颈我们束手无策。我们看到的是这些问题的现象,但是却很难看清本质,所有这些问题的本质就是软件运行背后的机理及支撑软件运行的各种平台和工具,假如能深入理解这些机制,那么处理这些问题就能够游刃有余,收放自如了。

编译流程分析

现在我们通过一个C语言的经典例子,来具体理解一下这些机制:

#include <stdio.h>int main(){    printf("Hello World");    return 0;}

在linux下只要要一个简单的命令(假设源代码文件名为hello.c):

$ gcc hello.c$ ./a.outHello World

其实上述过程可以分解为四步:

  • 预解决(Prepressing)
  • 编译(Compilation)
  • 汇编(Assembly)
  • 链接(Linking)

预编译

首先是源代码文件hello.c和相关的头文件(如stdio.h等)被预编译器cpp预编译成一个.i文件。第一步预编译的过程相当于如下命令(-E 表示只进行预编译):

$ gcc –E hello.c –o hello.i

还可以下面的表达

$ cpp hello.c > hello.i

预编译过程主要解决源代码文件中以”#”开头的预编译指令。比方#include、#define等,主要解决规则如下:

  • 将所有的#define删除,并开展所有的宏定义
  • 解决所有条件预编译指令,比方#if,#ifdef,#elif,#else,#endif
  • 解决#include预编译指令,将被包含的文件插入到该预编译指令的位置。
  • 删除所有的注释///**/
  • 增加行号和文件名标识,比方#2 “hello.c” 2。
  • 保留所有的#pragma编译器指令

截图个大家看看效果

经过预编译后的文件(.i文件)不包含任何宏定义,由于所有的宏已经被开展,并且包含的文件也已经插入到.i文件中,所以当我们无法判断宏定义能否正确或者头文件包含能否正确时,可以查看预编译后的文件来确定问题。

编译(compliation)

编译过程就是把预解决完的文件进行一系列的:词法分析语法分析语义分析优化后生产相应的汇编代码文件,此过程是整个程序构建的核心部分,也是最复杂的部分之一。其编译过程相当于如下命令:

$ gcc –S hello.i –o hello.s

通过上图我们不难得出,通过命令得到汇编输出文件hello.s.

汇编(assembly)

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎对应一条机器令。所以汇编器的汇编过程相对于编译器来讲比较简单,它没复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表逐个翻译即可以了。其汇编过程相当于如下命令:

as hello.s –o hello.o

或者者

gcc –c hello.s –o hello.o

或者者使用gcc命令从C源代码文件开始,经过预编译、编译和汇编直接输出目标文件:

gcc –c hello.c –o hello.o

链接(linking)

链接通常是一个让人比较费解的过程,为什么汇编器不直接输出可执行文件而是输出一个目标文件呢?为什么要链接?下面让我们来看看怎样样调用ld才可以产生一个能够正常运行的Hello World程序:

注意默认情况没有gcc / 记得 :$ brew install gcc

链接相应的库

下面在贴出我们的写出的源代码是如何变成目标代码的流程图:

主要通过我们的编译器做了以下任务:扫描、语法分析、语义分析、源代码优化、代码生成和目标代码优化

到这我们即可以得到以下的文件,不知道你能否有和我一起操作,玩得感觉还是不错,继续往下面看

iOS的编译器

iOS现在为了达到更牛逼的速度和优化效果,采用了LLVM

  • 1.LLVM核心库:
    LLVM提供一个独立的链接代码优化器为许多流行CPU(以及少量不太常见的CPU)的代码生成支持。这些库是围绕一个指定良好的代码表示构建的,称为LLVM中间表示(“LLVM IR”)LLVM还可以充任JIT编译器 - 它支持x86 / x86_64和PPC / PPC64程序集生成,并具备针对编译速度的快速代码优化。。

  • 2.LLVM IR 生成器Clang: Clang是一个“LLVM原生”C / C ++ / Objective-C编译器,旨在提供惊人的快速编译(例如,在调试配置中编译Objective-C代码时比GCC快3倍),非常有用的错误和警告消息以及提供构建优秀源代码工具的平台。

  • 3.LLDB项目:
    LLDB项目以LLVMClang提供的库为基础,提供了一个出色的本机调试器。它使用Clang AST表达式解析器LLVM JIT,LLVM反汇编程序等,以便提供“正常工作”的体验。在加载符号时,它也比GDB快速且内存效率更高。

  • 4.libclibc++:
    libclibc++ ABI项目提供了C ++标准库的标准符合性和高性能实现,包括对C ++ 11的完全支持。

  • 5.lld项目:
    lld项目旨在成为clang / llvm的内置链接器。目前,clang必需调用系统链接器来生成可执行文件。

LLVM采用三相设计,前台Clang负责解析,验证和诊断输入代码中的错误,而后将解析的代码转换为LLVM IR,后台LLVM编译把IR通过一系列改进代码的分析和优化过程提供,而后被发送到代码生成器以生成本机机器代码。

编译器前台的任务是进行:

  • 语法分析
  • 语义分析
  • 生成中间代码(intermediate representation )

在这个过程中,会进行类型检查,假如发现错误或者者警告会标注出来在哪一行。

以上图解内容所做的是事情和gcc编译一模模一样样!

iOS程序-详细编译过程

  • 1.写入辅助文件:将项目的文件结构对应表、将要执行的脚本、项目依赖库的文件结构对应表写成文件,方便后面使用;并且创立一个 .app 包,后面编译后的文件都会被放入包中;
  • 2.运行预设脚本:Cocoapods 会预设少量脚本,当然你也可以自己预设少量脚原本运行。这些脚本都在 Build Phases中可以看到;
  • 3.编译文件:针对每一个文件进行编译,生成可执行文件 Mach-O,这过程 LLVM 的完整流程,前台、优化器、后台;
  • 4.链接文件:将项目中的多个可执行文件合并成一个文件;
  • 5.拷贝资源文件:将项目中的资源文件拷贝到目标包;
  • 6.编译 storyboard 文件:storyboard 文件也是会被编译的;
  • 7.链接 storyboard 文件:将编译后的 storyboard 文件链接成一个文件;
  • 8.编译 Asset 文件:我们的图片假如使用 Assets.xcassets 来管理图片,那么这些图片将会被编译成机器码,除了 iconlaunchImage
  • 9.运行 Cocoapods 脚本:将在编译项目之前已经编译好的依赖库和相关资源拷贝到包中。
  • 10.生成 .app
  • 11.将 Swift 标准库拷贝到包中
  • 12.对包进行签名
  • 13.完成打包

编译过程确实是个比较复杂的过程,还有链接!并不是说难就不需要掌握,我个人建议每一个进阶路上iOS开发人员,都是要理解一下的。不需要你多么牛逼,但是你能在平常的交流探讨,面试中能点出一个两个相应的点,我相信绝对是逼格满满!

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » iOS开发你不知道的事-编译&链接

发表回复