iOS底层探索(二) - 写给小白看的Clang编译过程原理
iOS底层探索(一) - 从零开始认识Clang与LLVM
写在前面
编译器是属于底层知识,在日常开发中少有涉及,但在我的印象中,越接近底层是越需要编程基本功,也是越复杂的。但要想提升技术却始终绕不开要对底层原理的探究,很多资料都是直接抛出一堆函数概念和一顿操作,基础一般的小伙伴看了表示一脸懵逼。在此结合我自己的理解进行优化总结一下。毕竟知识水平有限,有问题或总结不妥的地方欢迎指出,多多学习,非常感谢!2018.2
入门起步
- 经过上一篇对编译器的基本介绍,相信大家对Clang都有一个基本的认识了,通俗来说是一个编译器的前端,负责分析源代码(就是我们使用的C/OC/C++等)。
Clang的编译过程
1.预处理
预处理顾名思义是预先处理,那预处理都做了哪些事情呢?内容如下。
(1) import 头文件替换
面向对象编程的思维下,我们写代码会经常用到其他类的属性\方法等,我们只需要导入头文件就可以用了,如:
#import <Foundation/Foundation.h> // 这里将会在预处理时会把 Foundation.h 文件的内容拷贝过来并替换 复制代码
基于这个原理,这里引出了一个小问题,如果
ClassA.h
文件引用了ClassB.h
,并且ClassB.h
也引用了ClassA.h
,这里是不是就会互相循环引入了?- 解决办法是在头文件中使用
@class ClassA; 复制代码
- 代替
#import "ClassA.h" 复制代码
- 这么写意思是声明
ClassA
是一个类,这样你就可以使用ClassA
做类名了,如果需要使用ClassA
的方法属性等可以在 .m 实现文件中再通过import MyClass.h
的方式使用,这种方法不但可以解决互相引入的问题还可以优化编译速度。
(2) macro 宏展开
无参宏: 如:
#define DATA_TYPE_NUM @"number" 复制代码
在此宏定义作用域内,输入了 DATA_TYPE_NUM,在预处理过程中 DATA_TYPE_NUM 都会被替换成 @"number"。
带参宏: 带参数的宏 如:
#define CYXColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0] 复制代码
(3) 处理其他的预编译指令(其实预编译过程也是出了预编译指令的过程)
条件编译语句也是在预处理阶段完成,并且条件编译只允许编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率,如以下代码就只会保留一个return语句:
#if DEBUG return YES; #elsereturn NO; #endif 复制代码
(4) 总结:
- 简单来说,“#”这个符号是编译器预处理的标志, 以下是一些常用的预处理指令参考
预处理指令 用法解析 #undef 取消已定义的宏 #if 如果给定条件为真,则编译以下代码 #ifdef 如果宏已经定义,则编译以下代码 #ifndef 如果宏没有定义,则编译以下代码 #elif 如果前面的#if给定条件不为真,当前条件为真,则编译以下代码 #endif 结束一个#if……#else条件编译块
*PS:还需要了解更多关于预编译的内容,还请自行百度*
[图片上传失败...(image-cf6f6f-1531632712782)][图片上传失败...(image-fd9112-1531632712782)]`$clang -E main.m`
复制代码
2. Lexical Analysis - 词法分析(输出token流)
- 预处理完成了以后,开始词法分析。词法分析其实是编译器开始工作真正意义上的第一个步骤,其所做的工作主要为将输入的代码转换为一系列符合特定语言的词法单元,这些词法单元类型包括了关键字,操作符,变量等等。举个例子:
Objective-C语言包含了关键字if、else、new等,那么在词法分析步骤时,遇到i与f或n与e与w组合在一起的时候,需要将这几个字母组合为关键字if或new这个词法单元。
- 词法分析,只需要将源代码以字符文本的形式转化成Token流的形式,不涉及交验语义,不需要递归,是线性的。
什么是token流呢?可以这么理解:就是有"类型",有"值"的一些小单元。
- 再举个例子:
比如一个运算表达式:
(28 + 78) * 2
这里面只需要解析出(
是一个开括号,28
是数字整形,+
是一个运算符号即可。
编译指令: $clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
3.Semantic Analysis - 语法分析(输出(AST)抽象语法树)
编译指令:$clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
语法分析的最终产物是输出抽象语法树
语法分析,在Clang中由Parser和Sema两个模块配合完成
交验语法是否正确
根据当前语言的语法,生成语意节点,并将所有节点组合成抽象语法树(AST)
这一步跟源码等价,可以反写出源码
Static Analysis 静态分析
- 通过语法树进行代码静态分析,找出非语法性错误
- 模拟代码执行路径,分析出control-flow graph(CFG) 【MRC时代会分析出引用计数的错误】
- 预置了常用Checker(检查器)
未完待续 ...
这是上篇,为保证博客质量与阅读体验(个人感觉一次阅读过多文字有点影响阅读体验),先分享已完成的上半部分,下篇将继续介绍Clang编译过程中的剩下环节,欢迎持续关注,感谢理解与支持!2018.2
预告:下篇将继续介绍Clang与LLVM以下环节的相关知识。
下面是一些关键词,有兴趣的朋友先自行谷歌学习吧,下篇等我有闲情的时候再更新了,我也不知道什么时候。2018.7.15
4. CodeGen - (Intermediate Representation,简称IR)IR中间代码生成
- CodeGen 负责将语法树丛顶至下遍历,翻译成LLVM IR
- LLVM IR 是Frontend的输出,也是LLVM Backend的输入,前后端的桥接语言 (Swift也是转成这个)
- 与 Objective-C Runtime 桥接
- Class/Meta Class/Protocol/Category内存结构生成,并存放在指定section中(如Class:_DATA, _objc_classrefs)
- Method/lvar/Property内存结构生成
- 组成method_list/ivar_list/property_list并填入Class
- Non-Fragile ABI:为每个Ivar合成OBJC_IVAR_$_偏移值常量
- 存取Ivar的语句(ivar = 123; int a = ivar;)转写成base + OBJC_IVAR$_的形式
- 将语法树中的ObjcMessageExpr翻译成相应版本的objc_msgSend,对super关键字的调用翻译成objc_msgSendSuper
- 根据修饰符strong/weak/copy/atomic合成@property 自动实现的 setter/getter
- 处理@synthesize
- 生成block_layout的数据结构
- 变量的capture(__block/__weak)
- 生成_block_invoke函数
- ARC:分析对象引用关系,将objc_storeStrong/objc_storeWeak等ARC代码插入
- 将ObjCAutoreleasePoolStmt转译成objc_autoreleasePoolPush/Pop
- 实现自动调用[super dealloc]
- 为每个拥有ivar的Class合成.cxx_destructor方法来自动释放类的成员变量,代替MRC时代的“self.xxx = nil”
5. Optimize - 优化IR
- 递归优化成伪递归
6. LLVM Bitcode - 生成字节码
7. Assemble - 生成Target相关汇编
- Assemble - 生成Target相关Object(Mach-O)
8. Link生成Executable
参考文档
https://zh.wikipedia.org/wiki/C%E9%A2%84%E5%A4%84%E7%90%86%E5%99%A8 https://llvm.org/docs/tutorial/LangImpl2.html https://www.objc.io/issues/6-build-tools/compiler/
转载于:https://juejin.im/post/5b4af8b0e51d45199060fdf8
iOS底层探索(二) - 写给小白看的Clang编译过程原理相关推荐
- iOS底层探索二(OC 中 alloc 方法 初探)
前言 相关文章: iOS底层探索一(底层探索方法) iOS底层探索三(内存对齐与calloc分析) iOS底层探索四(isa初探-联合体,位域,内存优化) iOS底层探索五(isa与类的关系) iOS ...
- iOS 底层探索篇 —— KVC 底层原理
iOS 底层探索篇 -- KVC 底层原理 1. Method Swizzling的坑与应用 1.1 method-swizzling 是什么? 1.2 坑点 坑点1:method-swizzling ...
- iOS 底层探索 - 消息转发
一.动态方法解析流程分析 我们在上一章<消息查找>分析到了动态方法解析,为了更好的掌握具体的流程,我们接下来直接进行源码追踪. 我们先来到 _class_resolveMethod 方法, ...
- 写给小白看的,逆向工程怎么上路?
什么是逆向工程 先给大家出一道思考题 用C语言设计一个程序,验证输入的密码是否是"12345678",如果验证成功,就输出"success",如果验证失败,则输 ...
- 写给小白看的线程和进程,高手勿入
计算机的核心是CPU,它承担了计算机的所有计算任务,CPU就像一个工厂,时刻在运行着,而操作系统管理着计算机,负责任务的调度.资源的分配和管理. 图片来源于网络 进程和线程都是计算机操作系统中的基本概 ...
- 写给小白看的硬核递归(低调点,当回小白)
本文首发公众号[bigsai] ,首发博客平台csdn,转载请找授权或附上原文链接. 大家好,我是bigsai,今天给大家讲讲递归.内容很丰富,看完感觉不错还请三连支持幸苦码字! 什么是递归? 递归: ...
- 【入门基础】写给小白看的入门级 Java 基本语法
众所周知,Java 是一门面向对象的编程语言.它最牛逼的地方就在于它是跨平台的,你可以在 Windows 操作系统上编写 Java 源代码,然后在 Linux 操作系统上执行编译后的字节码,而无需对源 ...
- OC底层探索(二十二)八大锁
OC底层文章汇总 锁的种类 在OC中锁分为互斥锁和自旋锁两种. 互斥锁 用于保护临界区,确保同一时间,只有一条线程能够执行 如果代码中只有一个地方需要加锁,大多都使用 self,这样可以避免单独再创建 ...
- 写给小白看的Mac入门教程
第1节.认识桌面 ①菜单栏 位于桌面的最上方,分为三个部分:苹果菜单.应用程序菜单.系统功能图标区. 苹果菜单:包含了系统相关的命令,可以将它想象成Windows下的"开始"按钮, ...
最新文章
- [转]HTTP协议详解
- 彻底解决web开发中遇到的路径问题(上)
- SpringMVC 异常统一处理
- EDAS ScheduleX 问题
- python文本分类_教你用python做文本分类
- VC6.0显示代码行号
- 记一次性能优化,限制tcp_timewait数量,快速回收和重用
- 如何找到点击Object ID 白屏问题的root cause
- MongoDB中关于64位整型存储解决方案
- 数据结构 —— 数据结构相关
- 4g通信模块怎么连接sim卡_4G通信模块在ARM平台下的应用
- SpringBoot @Cacheable注解的属性使用
- 【Elasticsearch】Elasticsearch 7 : 动态映射 dynamic
- appium在android7.0上无法启动问题
- 输出NSRange类型的数据
- window下安装scapy
- linux子系统gdp调试,GDB调试命令_Linux编程_Linux公社-Linux系统门户网站
- 拉格朗日插值法_Lagrange、Newton、分段插值法及Python实现
- 简单matlab插值函数
- 第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛 G	 旋转矩阵 【模拟】
热门文章
- 回调 that.setdata 数据不更新_重大利空落地,或损上亿利润,乐普医疗回调近四成...
- unity 里调试native code
- $.get、$.post 和 $().load()
- 动手动脑 - 继承与多态
- HDU4055 - number string(DP)
- 实时数据处理插件开发flume+kafka+storm:flume
- 数据结构——各排序算法的比较
- 混合模式程序集是针对“v2.0.50727”版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集...
- U3D-FSM有限状态机的简单设计
- linux网络编程系列-select和epoll的区别