1.前言
本篇文章旨在展示Clang的探究过程
探究Clang的意义
Clang在iOS当中的发展史
编译过程
学习Clang的实际应用
2.意义
- 1.oc、swift都是编译型语言,两者都采用Clang作为前端编译器,LLVM作为后端编译器,学习Clang了解程序的编译执行过程
- 2.探究代码的底层实现
- 3.了解mach-o,dylid,dSYM是什么
- 4.app瘦身、启动优化、lldb调试技巧、Clang插件定制
在学习戴铭
的课程中也多次提到了Clang对于开发的帮助,有兴趣的可以去学习下iOS高手开发课
3.Xcode中的编译器发展史
- 1.xcode3以前:GCC
- 2.xcode3:增加llvm,gcc前端+llvm后端
- 3.xcode4.2:出现clang-llvm3.0成为默认编译器
- 4.xcode4.6llvm升级到4.2版本
- 5.code5:gcc被废弃,新的编译器是llvm5.0,从gcc过渡到clang-llvm时代
参考指令:clang -v
4.编译过程
一次完整的编译流程:clang -ccc-print-phases main.m
0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output
2: compiler, {1}, ir
3: backend, {2}, assembler
4: assembler, {3}, object
5: linker, {4}, image
6: bind-arch, "x86_64", {5}, image
4.1预处理
xcrun clang -x objective-c -E -DDEBUG=1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m
在xocde中查看preprocess过程Product->Perform Action->Preprocess
4.2词法分析
clang -fmodules -fsyntax-only -Xclang -dump-tokens -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m
4.3语法分析
clang -fmodules -fsyntax-only -Xclang -ast-dump -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m
4.4生成中间代码
clang -O3 -S -fobjc-arc -emit-llvm -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m -o main.ll
O3
代码优化级别,optimization level
4.5生成目标文件
clang -fmodules -c -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.m -o main.o
4.6生成可执行文件
clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk main.o AppDelegate.o -o main
4.7Demo演示
新建一个C项目Demo ,演示生成可执行文件的过程
clang -fmodules -c main.c -o main.o
clang main.o -o main
./main
4.8Xcode查看编译过程
我们可以查看到编译的整个过程信息
5.查看oc的c++实现
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk AppDelegate.m
6.Mach-O
编译后的可执行文件,包括exec(刚才生成的main)、dylib(/usr/lib)
Mach-O 文件包含三个区域
1.Mach-O Header
:包含字节顺序,magic,cpu 类型,加载指令的数量等
2.Load Commands
:包含很多内容的表,包括区域的位置,符号表,动态符号表等。每个加载指令包含一个元信息,比如指令类型,名称,在二进制中的位置等
3.Section
:最大的部分,包含了代码,数据,比如符号表,动态符号表等。
辅助查看工具MachoView
7.LinkMap文件
Write Link Map File
设置为YES
,指定存储目录$(SRCROOT)/build/LinkMap.txt
,编译后该文件列出了编译后的每一个.o文件(包括静态库里的),以及每一个每一个目标文件的代码段,数据存储详情.默认:Build/Intermediates.noindex/ClangDemo.build/Debug-iphonesimulator/ClangDemo.build
__text
表示编译后的程序执行语句,__data
表示已初始化的全局变量和局部静态变量,__bss
表示未初始化的全局变量和局部静态变量,__cstring表示代码里的字符串常量,等等。
Address Size File Name
0x100001380 0x00000040 [ 2] +[ViewController createSark]
偏移地址 4*16byte 所属文件
8.学习Clang作用
8.1App瘦身方案
1.滴滴 基于clang插件的一种iOS包大小瘦身方案
https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247488360&idx=1&sn=94fba30a87d0f9bc0b9ff94d3fed3386&source=41#wechat_redirect
2.微信 iOS微信安装包瘦身
https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=207986417&idx=1&sn=77ea7d8e4f8ab7b59111e78c86ccfe66&3rd=MzA3MDU4NTYzMw==&scene=6#rd
3.阿里 减小ipa体积之删除frameWork中无用mach-O文件
https://www.oschina.net/question/2625381_2168321
可以看到以上方案都是基于编译过程/结果进行优化,可见Clang在app开发过程中起到的强大作用
8.2App启动优化
启动流程:
1.Load dylibs->rebase->bind->objc->initializers
读取app的可执行文件,Mach-o,在可执行文件的Mach_header查找lc_load_dylib的加载指令,查找需要的动态库dylib,
2.加载到内存的可执行文件都是不可用的,需要ASLR(进程每次启动,地址空间都会被简单的随机化,有PIE标识,otool -hv ClangDemo),需要rabase,binding
rebase:因为初始地址和内存地址不同,需要修正
binding:因为动态库不编译进程序最终的二进制文件中,而是在运行的时候动态的查找调用函数的地址
以上主要是__DATA中的指针数量
3.objc setup
libsystem中libsystem_initializer初始化libdispatch,调用了os_object_init,最终调用了objc_init,objc_init中绑定了3个方法,map_2_images,load_images,unmap_images:
map_2_images:Binding操作结束之后,发出dyld_image_state_bound通知,调用map_2_images,主要做以下几件事来完成objc setup:
读取二进制文件的DATA段内容,找到与objc相关的信息.
注册objc类
确保selector的唯一性
读取protocol以及category的信息
load_images:函数作用就是调用objc的load方法,它监听dyld_image_state_dependents-initialize通知
unmap_image可以理解为map_2_images的逆向操作.
以上3步都是修改__DATA segment中的内容.
4.静态初始化工作,例如load函数,c++的一些初始化构造函数
5.执行完上述的fix-ups之后,接着就会调用mian()
方法:
优化__data__,即去除无用的类,方法,属性,分类
9.DSYM是什么
从Mach-o文件中抽取调试信息(二进制地址对,源码文件,行号以及函数名字的对应关系)而得到的文件目录,实际用于保存调试信息的是.dSYM文件中的DWARF,可以手动生成.
查看.dSYM文件内容:dwarfdump -v ClangDemo.app.dSYM
10.Xcode中的clang
警告提示:Incomplete objective-c protocols
等等
11.lldb调试
app运行断点调试时,以下列出以下几个比较有用的lldb调试方法
-
thread return
直接跳出方法: -
expression
修改值 ,比如expression name = @"张三"
-
bt 10
堆栈打印10条,也可以直接bt
-
thread return
跳出方法