iOS编译过程

背景

随着需求爆发,代码和图片资源越来越多,包体积越来越大,用户下载成本越来越高,瘦包迫在眉睫,要想瘦包,就需要知道包由何组成,每个组成部分又是怎么来的,这就必须了解编译过程,当然有人会说,不就瘦包嘛,网上有教程呀,巴拉巴拉照着做就行了!
嗯~那证明app的包还不够复杂,当你需要引用上百个自研或是开源库的时候自会明白,我们来看下文件组成

iOS应用的文件目录结构

一个app通常有如下几个模块组成:源码编译后的二进制(静态库包含在其中)、动态库、bundle等资源文件、plist等配置文件、代码签名文件CodeResources

  • 源码编译后的二进制
    静态库是单独编译的,在源码文件编译完成后会由静态链接器一起打包生成最终的Mach-O格式的二进制文件,这就意味着多份相同的静态库是没有办法链接通过的,有一种办法可以解决这个问题,就是同一份静态库包装到不同的动态库中以形成隔离,因为动态库在app编译完成后不会一起进行静态链接并且符号表也是与app分开的,在集成三方库的时候可能会遇到这个问题

  • 动态库
    动态库也是单独编译的,我们程序中用到的所有系统级别的framework都是动态库,它们集成在操作系统中,不会占用app的存储空间,自研的动态库是集成在app目录下的Frameworks中,编译时,动态库的物理路径需要配置到app的编译设置的搜索路径,在App编译的时候就会被拷贝到app的压缩包里的Frameworks文件夹下,符号表单独保存并且需要上传到苹果的后台

  • bundle
    程序的图片资源文件

  • plist配置文件
    程序的标识符以及系统服务的访问权限等都配置在里面

  • 代码签名
    就是存储通过证书以及加密算法生成签名密钥的文件

可执行文件的物理结构

可执行文件包含了机器指令代码、数据、符号表、调试信息等,目标文件以的形式存储以上信息,目标文件通常包括:文件头、text段、data段、bss段

  • 文件头
    描述整个文件的属性(包含目标机器架构、是动态链接还是静态链接以及是否是可执行文件)和一个段表
    段表就是存储段的数组,描述了各个段在文件中的偏移量以及段本身的属性

  • text段
    源代码编译后的机器指令被存放在text段,又叫做代码段

  • data段
    初始化的全局变量和静态变量被放在data段,又叫做数据段

  • bss段
    未初始化的全局变量和静态变量被放在bss段,和data段一起被称为数据段

符号表
符号通常指函数和变量(还有其他符号比如段名、行号信息等),编译的最后一个阶段静态链接,就是符号的处理过程,每个目标文件都有一个符号表,记录了目标文件里面所有的符号以及对应的符号值,对于函数和变量来讲符号值就是他们的地址,符号表在程序编译之初就创建,在整个编译过程中都起着至关重要的作用,在静态链接完成之后会生成最终的符号表,动态库由于不参与主可执行文件的静态链接过程,所以符号表是抽离的

调试信息
编译器支持源代码级别的调试,debug环境下可以在程序中打断点调试,release环境下编译器会过滤掉调试信息,即编译后的二进制里面没有调试代码,因此断点是无效的

可执行程序二进制(包含了静态库)、动态库都是编译器的产物,那么为什么需要编译呢,源码不可以直接执行吗

Object-C为什么需要编译

  • 解释型语言
    解释型语言运行时实时被解释器解析为机器码并且执行一次就需要解析一次,脚本语言如JavaScript等都是解释型语言,优点是省去了编译过程,但是运行效率低,虽然当代浏览器解释器经过深度优化,但是相比直接运行可执行二进制来讲也会慢很多
  • 编译型语言
    有一个复杂又耗时的编译过程,最终生成的二进制机器码,可以被处理器直接识别并执行,相比解释性语言来说运行时效率高了很多

Object-C是一门编译型语言,底层通过c、c++、汇编实现,上层架设了一层语法糖,配合强大的运行时库使用

编译原理

一句话概括:从源码生成二进制机器码的过程就是编译过程,整个过程分为四个阶段:预编译、编译、汇编、静态链接

  • 预编译:
    进行宏替换、头文件包含、条件编译识别等工作
  • 词法分析:
    将源代码的字符序列分割成一些列的记号,包含关键字、标识符、字面量、符号,同时将标识符存放到符号表,常量存放到文字表以备后续使用
  • 语法分析:
    把上一步产生的记号解析成一个以表达式为节点的抽象语法树(AST),这个阶段会进行表达式合法性校验,比如缺少操作符、括弧不匹配等编译器会直接给出错误提示,当然xcode在没有bulid之前也会进行语法检测,这也是衡量一款开发工具是否合格的基本要求
  • 语义分析:
    编译器只能分析静态语义,即编译期间就可以确定的语义,通常包括类型匹配、转换,这个阶段编译器会给出类型不匹配等警告,不会报错,因为运行时基本类型不匹配实际会进行精度取舍,指针类型不匹配会以指针所指向的真实对象去调用方法,这些都是动态语义,不在编译器的管理范畴,当然也没有这个能力
  • 中间代码生成:
    由于直接在语法树上做优化比较困难,编译器会将整个语法树转换成中间代码,它是语法树的顺序表示,至此已经生成接近目标代码的汇编代码,只是还与目标机器的运行时环境无关,比如机器字长、变量地址、寄存器名称等,因此中间代码是编译前后端的分割线,前端负责产生中间代码,后端负责将其转换为目标代码,这也使得编译器跨平台成为可能
  • 目标代码生成与优化:
    目标代码生成器会根据目标机器的运行环境将中间代码转换为目标代码,目标代码优化器会对汇编码进行优化,比如循环优化、多余指令删除、寻址优化、用位移操作代替乘法运算等
  • 汇编:
    汇编器是编译后端的后端,负责将汇编码转化为机器码
  • 静态链接:
    编译器会把源代码编译成一个一个的目标文件.o文件,定义在A.o文件中的变量及函数在B.o文件中是无法得到地址的,编译器会用0填充,待到链接的时候由链接器进行修正,这个过程叫做重定位,最终静态链接器把编译产生的所有.o文件和静态库一起打包生成一个Mach-O格式的二进制文件

减小包体积

我们以目标为导向,瘦包需要瘦哪些模块:可执行二进制文件、动态库、Bundle资源

  • 第一招:✔️
    前面优化图片资源会带来很多惊喜,扫描无用资源文件直接删除和压缩Bundle中的图片,可能分分钟降下来几十兆,一期目标直接达成,接下来就要对代码部分动刀了
  • 第二招:❌
    经过上面的编译原理我们知道不同架构的目标机器编译出来的目标代码是不一样的,iOS的app是一个胖架构包,可以包含很多架构在里面,打包送上应用商店后苹果会对包进行拆分,不同架构的手机下载对应架构的包,因此减少app兼容的架构是不能够减小包体积的
  • 第三招:✔️❌参半
    扫描删减无用代码,合并类似功能的冗余代码,这招对于源文件、动态库都会奏效,对于静态库来讲未必奏效,因为静态库在链接的时候只有用到的部分才会打包到可执行二进制
  • 第四招:✔️❌需要根据包本身的体积衡量
    多个动态库引用了相同一份静态库会分别在动态库中链接到使用的部分,换句话说静态库被动态库使用的部分会打包到动态库中,因此多个动态库就打包了多份静态库(当然只有使用到的部分),这里可以做个优化把静态库变成动态库,这样就是所有动态库引用一份动态库,不会出现多份冗余的情况,当然要考虑到库包本身的大小,如果太大做成动态库反而会增大包体积,因为动态库是全量拷贝到app包内的
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • iOS 开发中 Objective-C 是 Clang / LLVM 来编译的。swift 是 Swift / L...
    forping阅读 946评论 0 0
  • 前言 语言类型 我们有很多维度可以将计算机语言进行分类,其中以编译/执行方式为维度,可以将计算机语言分为: 编译型...
    AiLearn阅读 2,385评论 1 6
  • 前言 一般可以将编程语言分为两种,编译语言和直译式语言。像C++,Objective C都是编译语言。编译语言在执...
    cosWriter阅读 2,316评论 1 28
  • 前言 一般可以将编程语言分为两种,编译语言和直译式语言。 像C++,Objective C都是编译语言。编译语言在...
    一川烟草i蓑衣阅读 10,317评论 1 27
  • 来源:黄文臣 前言 一般可以将编程语言分为两种,编译语言和直译式语言。像C++,Objective C都是编译语言...
    哦呵呵y阅读 1,256评论 0 6