[iOS]|[iOS] 编译相关知识
目录:
- mach-O
- index做了什么
- architecture
- jenkins如何自动打包
- 打包release debug证书区别
- LLVM & bitcode
Mach-O 为 Mach Object 文件格式的缩写,它是一种用于可执行文件、目标代码、动态库、内核转储的文件格式。作为 a.out 格式的替代,Mach-O 提供了更强的扩展性,并提升了 符号表 中信息的访问速度。
- 通用二进制
另外一种类型就是只编写一个架构的代码,当另外一种处理环境时让系统自动调用模拟器运行,这会导致运行速度下降。
- 多重架构二进制(胖二进制)
有一些可以用于查看文件的类型的命令,以及合并不同mach-o文件:(这部分好像主要是逆向工程会用到,将来再多学一下~)
//命令查看 Mach-O 文件类型
file Mach-O //命令查看文件架构
lifo -info //拆分各种架构
lipo -thin <架构名> -output <输出文件路径> //合并各种架构
lipo -create -output <输出文件路径>
2. index做了什么 不知道你有木有发现,在build过程开始的时候,或者偶尔Xcode就会出现
indexing
。XCode的indexing系统消耗太大,这会导致在调试时经常会卡住。它带来的好处很多,比如自动补全、查找定义等,如果想关闭可以在terminal输入:
defaults write com.apple.dt.XCode IDEIndexDisable 1
还原的话就将1改为0即可。
那么indexing是在做什么嘞?你可以到
~/Library/Developer/Xcode/DerivedData
目录下会看的一个index文件夹,也就是indexing生成的。index其实就是项目的索引,它会将我们代码中用到的类/消息和实际实现、声明关联起来,也就帮助了我们可以用xcode jump to definition。其实这个definition和我们代码中的消息直接的关联,是通过索引记录的。
3. architecture iOS的App现在基本都是用llvm在编译,Xcode也提供了各种设置帮助你进行编译参数的设定。里面有一项就是设定编译的体系结构,涉及到的参数包 括:Architectures、Valid Architectures和Build Active Architecture Only。
- Architectures:这是指你想支持的指令集,比如:armv7,armv7s,或者可以用$(ARCHS_STANDARD_32_BIT)这样的参数
- Valid Architectures:这是指你即将编译的指令集
- Build Active Architecture Only:是否只编译当前设备适用的指令集(如果这个参数设为YES,那么如果你用iPhone 6调试,最终生成的一个支持arm64指令集的Binary;如果你用iPhone4编译,最终生成一个支持armv7指令集的Binary,所以一般在 DEBUG模式下设为YES,RELEASE设为NO)
这里,我们编译最终支持的指令集是Architectures和Valid Architectures两个参数的交集。另外,列一下目前常见iOS设备的指令集:
ARMv8/ARM64: iPhone 6(Plus), iPhone 5s, iPad Air(2), Retina iPad Mini(2,3)
ARMv7s: iPhone 5, iPhone 5c, iPad 4
ARMv7: iPhone 3GS, iPhone 4, iPhone 4S, iPod 3G/4G/5G, iPad, iPad 2, iPad 3, iPad Mini
ARMv6: iPhone, iPhone 3G, iPod 1G/2G
ARM处理器,特点是体积小,低功耗,低成本,高性能,所以几乎所有手机处理器都基于ARM,在嵌入式系统中应用广泛。
armv6 | armv7 | armv7s | arm64都是ARM处理器的指令集,这些指令集都是向下兼容的,例如armv7指令集兼容armv6,只是armv6的时候无法发挥出其性能,无法使用armv7的新特性,从而会导致程序执行效率没那么高。
还有两个我们也很熟悉的指令集,i386 | x86_64是Mac处理器的指令集,i386是针对intel通用微处理器32架构的。x86_64是针对x86架构的64位处理器。所以当使用iOS模拟器的时候会遇到i386 | x86_64,iOS模拟器没有arm指令集。
我们用的一个SDK是不支持x86/i386的,所以不能在模拟器上使用那个库,可以通过宏定义在虚拟器上不引入该库。
#if TARGET_IPHONE_SIMULATOR#else
//第三方的相关代码
#endif
支持的指令集越多,就会编译出包含多个指令集代码的数据包,对应生成二进制包就越大,也就是ipa包会变大。例如,将Architectures支持arm指令集设置为:armv7,armv7s,对应的Valid Architectures的支持的指令集设置为:armv7s,arm64,那么此时,Xcode生成二进制包所支持的指令集只有armv7s。
Build Active Architecture Only
当其值设置为YES,是为了debug的时候编译速度更快,它只编译当前的architecture版本,而设置为NO时,会编译所有的版本。编译出的版本是向下兼容的,连接的设备的指令集匹配是由高到低(arm64 > armv7s > armv7)依次匹配的。比如你设置此值为yes,用iPhone4编译出来的版本是armv7版本的,iPhone5也可以运行,但是armv6的设备就不能运行。所以,一般debug的时候可以选择设置为yes,release的时候要改为NO,以适应不同设备。
4. jenkins如何自动打包 可参考:https://www.jianshu.com/p/3668979476ad 以及 https://www.jianshu.com/p/5af631ba513d
这部分我也没细看,就是大概看了一下,本地需要搭一个Jenkins环境我就太懒了,之前很好奇怎么搞的,有机会下次自己搞个试一下~
5. 打包release debug证书区别 这个真的不太熟,是偶尔看到的:https://blog.csdn.net/weixin_34221112/article/details/92325150
所以Release版本如果"Debug executable"就可以断点,但无法使用Assert、Trace等功能。因此,Release版本下调试操作实际并没有任何意义,调试结果没有任何参考价值。
development和distribution证书:development证书开发时使用,distribution证书发布时使用。它们跟Xcode的debug和release没有一毛钱关系。development证书允许开发者将app直接"Run"进真机,而distribution证书则不可以。这就是它们直接的主要区别。
6. LLVM & bitcode 我们可以认为LLVM是一个完整的编译器架构,也可以认为它是一个用于开发编译器、解释器相关的库。可参考:https://www.cnblogs.com/CoderHong/p/8983917.html
在理解LLVM时,我们可以认为它包括了一个狭义的LLVM和一个广义的LLVM。
广义的LLVM其实就是指整个LLVM编译器架构,包括了前端、后端、优化器、众多的库函数以及很多的模块;而狭义的LLVM其实就是聚焦于编译器后端功能(代码生成、代码优化、JIT等)的一系列模块和库。
文章图片
LLVM 【[iOS]|[iOS] 编译相关知识】目前iOS 开发中 Objective-C 和 Swift 都用的是 Clang / LLVM 来编译的。Clang 是 LLVM 的子项目,是 C,C++ 和 Objective-C 编译器,目的是提供惊人的快速编译,比 GCC 快3倍。
LLVM 核心库提供一个优化器,对流行的 CPU 做代码生成支持。lld 是 Clang / LLVM 的内置链接器,clang 必须调用链接器来产生可执行文件。
源文件从编译到生成可执行文件流程大致如下图
文章图片
源码到可执行文件 LLVM的子项目:
-
LLVM Core
提供了一个现代的源代码和目标独立优化器, 以及许多流行的 CPU (甚至是一些不太常见的处理器) 的汇编代码生成支持。 - Clang
一个 C/C++/Objective-C 编译器,致力于提供令人惊讶的快速编译,极其有用的错误和警告信息,提供一个可用于构建很棒的源代码级别的工具。 -
Dragonegg
GCC 插件,可将 GCC 的优化和代码生成器替换为 LLVM 的相应工具。 - LLDB
基于LLVM提供的库和Clang构建的优秀的本地调试器。 -
libc++、libc++ ABI
符合标准的,高性能的C++标准库实现,以及对 C++11 的完整支持。 -
compiler-rt
针对 __fixunsdfdi 和其他目标机器上没有一个核心 IR(intermediate representation) 对应的短原生指令序列时,提供高度调优过的底层代码生成支持。 -
OpenMP
Clang 中对多平台并行编程的runtime支持。 -
Vmkit
基于 LLVM 的 Java 和 .NET 虚拟机实现。 -
Polly
支持高级别的循环和数据本地化优化支持的 LLVM 框架。 -
libclc
OpenCL 标准库的实现 -
Klee
基于 LLVM 编译基础设施的符号化虚拟机 -
SAFECode
内存安全的C/C++编译器 -
lld
Clang/LLVM 内置的链接器
- 不同的前端后端使用统一的中间代码LLVM Intermediate Representation (LLVM IR)
- 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
- 如果需要支持一种新的硬件设备,那么只需要实现一个新的后端
- LLVM现在被作为实现各种静态和运行时编译语言的通用基础结构(GCC家族、Java、.NET、Python、Ruby、Scheme、Haskell、D等)
新建项目默认就打开了bitcode设置。而且大部分开发者都被这个突如其来的bitcode功能给坑过导致项目编译失败,而这些因为bitcode而编译失败的的项目都有一个共同点,就是链接了第三方二进制的库或者框架,而这些框架或者库恰好没有包含bitcode的东西(暂且称为东西),从而导致项目编译不成功。所以每当遇到这个情况时候大部分人都是直接设置Xcode关闭bitcode功能,全部不生成bitcode。也不去深究这一开关背后隐藏的原理。
文章图片
bitcode开关
Bitcode就是LLVM的IR中间码。LLVM的编译工作原理是前端负责把项目程序源代码翻译成Bitcode中间码,然后再根据不同目标机器芯片平台转换为相应的汇编指令以及翻译为机器码。也就是说现在打开Bitcode功能提交一个App到应用商店,以后如果苹果新出了一款手机并CPU也是全新设计的,在苹果后台服务器一样可以从这个App的Bitcode开始编译转化为新CPU上的可执行程序,可供新手机用户下载运行这个App。
然而bitcode他也不是完全独立于处理器平台和调用约定的。寄存器的大小在指令集中是一个相当重要的特性,64bit寄存器可以比32bit寄存器存储更多的数据,生成64bit平台的bitcode和32bit平台的bitcode是明显不同的。
到此,让我们思考一下,为什么苹果默认要求watchOS和tvOS的App要上传bitcode?但是bitcode给开发者带来的不便之处就是: 没用bitcode之前,当应用程序奔溃后,开发者可以根据获取的的奔溃日志再配上上传到苹果服务器的二进制文件的调试符号表信息可以还原程序运行过程到奔溃时后调用栈信息,对问题进行定位排查。但是用了bitcode之后,用户安装的二进制不是开发者这边生成的,而是苹果服务器经过优化后生成的,其对应的调试符号信息丢失了,也就无法进行前面说的还原奔溃现场找原因了。
因为把bitcode上传到他自己的中心服务器后,他可以为目标安装App的设备进行优化二进制,减小安装包的下载大小,当然iOS开发者也可以上传多个版本而不是打包到单个包里(胖二进制每种arch都要有可执行文件),但是这样会占用更多的存储空间。最重要的是允许苹果可以在后台服务器对应用程序进行签名,而不用导出任何密钥到终端开发者那。
目前,watchOS和tvOS应用发布必须上传带bitcode版本的包。iOS应用发布对bitcode的要求是可选的,用户可以在Xcode的项目设置中关闭。 相当于在编译的时候加一个标记
embed-bitcode-marker(调试构建) embed-bitcode(打包/真机构建)
。这个在clang编译器的参数是-fembed-bitcode
,swift编译器的参数是-embed-bitcode
。推荐阅读
- 2020-04-07vue中Axios的封装和API接口的管理
- iOS中的Block
- 记录iOS生成分享图片的一些问题,根据UIView生成固定尺寸的分享图片
- 2019-08-29|2019-08-29 iOS13适配那点事
- Hacking|Hacking with iOS: SwiftUI Edition - SnowSeeker 项目(一)
- iOS面试题--基础
- 接口|axios接口报错-参数类型错误解决
- iOS|iOS 笔记之_时间戳 + DES 加密
- 思维导图作业3—工作相关导图
- iOS,打Framework静态库