戴铭(iOS开发课)读书笔记:0506章节-编译提速

原文链接:链接器:符号是怎么绑定到地址上的?
原文链接:App 如何通过注入动态库的方式实现极速编译调试?


05 章节 链接器:符号是怎么绑定到地址上的?

这篇文章主要介绍链接器的相关知识,从底层找答案,解决项目编译速度的问题。
由于理论知识为主,我只是把相关的知识点做总结梳理,方便自己记忆和学习,如果想要看完整的内容还需阅读原文

一、iOS 为什么使用编译器?而不是解释器?

编译器就是将“一种语言(通常为高级语言)”翻译为“另一种语言(通常为低级语言)”的程序。

一个现代编译器的主要工作流程:源代码 (source code) → 预处理器 (preprocessor) → 编译器 (compiler) → 目标代码 (object code) → 链接器(Linker) → 可执行程序 (executables)

例如 C、C++、Objective-C甚至汇编语言都是上面所提到的高级语言。

iOS之所以不使用解释器来运行代码,是因为苹果公司希望iPhone的执行效率更高、运行速度更快。

那么什么是解释器呢?

解释器(英语:Interpreter),又译为直译器,是一种电脑程序,能够把高级编程语言一行一行直接转译运行。解释器不会一次把整个程序转译出来,只像一位“中间人”,每次运行程序时都要先转成另一种语言再作运行,因此解释器的程序运行速度比较缓慢。它每转译一行程序叙述就立刻运行,然后再转译下一行,再运行,如此不停地进行下去。

例如:PythonTCL和各种Shell程序一般而言是使用解释器执行的

那么,使用编译器和解释器执行代码的特点概括:

  • 采用编译器生成机器码执行的好处是效率高,缺点是调试周期长。
  • 解释器执行的好处是编译调试方便,缺点是执行效率低。
编译器和解释器对比
二、iOS开发使用的什么编译器?编译过程如何?

苹果公司现在使用的编译器是 LLVM(Xcode 5 之前使用的 GCC)。

编译的主要过程:
1 LLVM 预处理你的代码,比如把宏嵌入到对应的位置。
2 预处理后,LLVM 会对代码进行词法分析和语法分析,生成 AST 抽象语法树。
3 最后 AST 会生成一种更接近机器码的语言 IR。通过 IR 可以生成多份适合不同平台的机器码。对于 iOS 系统,IR 生成的可执行文件就是 Mach-O。

编译的主要过程
三、iOS系统的链接器?编译时链接器做了什么?

LLVM 其实是编译器工具链技术的一个集合。其中的 lld 项目就是内置链接器。

链接器的作用是完成变量、函数符号和其地址绑定的任务。

不使用链接器会怎么样?

不使用链接器的话,首先你就需要在写代码时给每个指令设好内存地址,就好像直接在和不同平台的机器沟通,可读性和可维护性很差。其次这会导致代码和内存地址绑定得太早,也就是说你需要针对不同的平台写多份代码,何必呢?

链接器为什么要把项目中的多个 Mach-O 文件合并成一个?

因为单个文件的 Mach-O 文件是无法正常运行的,如果运行时碰到调用在其他文件中实现的函数的情况时,就会找不到这个调用函数的地址,从而无法继续执行。

最后总结一下链接器对代码主要做了哪几件事
1 去项目文件里查找目标代码文件里没有定义的变量。
2 扫描项目中的不同文件,将所有符号定义和引用地址收集起来,并放到全局符号表中。
3 计算合并后长度及位置,生成同类型的段惊醒合并,建立绑定。
4 对项目中的不同文件里的变量进行机制地址重定位。
而且,链接器可以通过打开 Dead code stripping 开关,来开启自动去除无用代码的功能。

四、动态库链接

在真实的开发环境中,我们需要用到很多现成的共用库(例如 GUI 框架、网络框架等),这些共用库又分为静态库和动态库两种:

  • 静态库是编译时链接到你的 Mach-O 文件的,无法动态加载和更新。
  • 动态库是运行时链接的库,并没有参与 Mach-O 文件的编译和链接。使用 dyld 可以实现动态加载

关于 dyld 的具体作用,你可以查看 原文 或者这篇博客:Dynamic Linking On OS X

简单总结,dyld 做了什么:
1 先执行 Mach-O 文件,根据 Mach-O 文件里的 undefined 的符号加载对应的动态库,系统会设置一个共享缓存来解决加载的递归依赖问题。
2 加载后,将 undefined 的符号绑定到动态库里对应的地址上。
3 最后再处理 +load 方法,main函数返回后运行 static terminator。

五、最后

理解了程序从编译、链接、执行、动态库加载到main函数执行的过程,再分阶段的思考和优化。

编译阶段:每个文件独立编译成 Mach-O 文件等待链接。那么编译器可以根据你修改的文件范围来减少编译,从而提高每次编译的速度。

链接阶段:文件越多,链接器所需执行的遍历操作就会越多,从而降低编译速度。

动态库加载阶段:在修改代码之后,尝试不去链接项目中的所有文件,只编译当前修改的文件动态库,通过运行时加载动态库的方式达到及时更新的效果。甚至通过逆向的思维,直接将别人的功能模块作为动态库加载到自己的app中。

06 章节 App 如何通过注入动态库的方式实现极速编译调试?


这个章节就是对于上篇文章,在最后的 动态库加载阶段 通过注入动态库的方式实现极速编译。

原文主要介绍了一款名为 Injection for Xcode 的开源工具的使用和原理。

开源地址:https://github.com/johnno1962/InjectionIII
同时我也找到了作者发布的视频演示案例:http://artsy.github.io/blog/2016/03/05/iOS-Code-Injection/

你可以快速尝试:
1 在App Store下载InjectionIII。
2 打开Injectionlll,选择打开项目的根目录。
3 在项目 AppDelegate 的 applicationDidFinishLaunching(_ application: UIApplication) 加入下面代码:

#if DEBUG
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle")?.load()
#endif

Xcode 10.1:

#if DEBUG
Bundle(path: "/Applications/InjectionIII.app/Contents/Resources/iOSInjection10.bundle")?.load()
#endif

4 在需要变更的控制器中添加监听

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.injected(_:)), name: Notification.Name(rawValue: "INJECTION_BUNDLE_NOTIFICATION"), object: nil)

5 实现 injected: 方法,你可以在这里修改代码:

@objc func injected(_ notification: NSNotification) {
    // to do ...
}

接下来,你的每次 command + s 都可以触发 injection watching了。

最后分享一张 Injection 的工作原理图,帮助大家理解。


这部分的内容太过于底层了,需要大量的时间去消化和理解。先做到了解相关的知识,以后有时间再慢慢消化。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,779评论 0 27
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,089评论 1 32
  • 前言 说到动态库,就不得不提静态库。静态库可以看做是一个具有特定功能的代码块,如果app中引用了静态库,则在编译时...
    wangzzzzz阅读 5,315评论 6 13
  • 这是冬夜。我把他拎起 放在膝盖,打开 木盆中的洗脚水,一忽儿热,一忽儿冷 我感觉到冷 书一页一页翻过去, 木盆上空...
    汪继先阅读 387评论 0 2
  • 时间是挺快的,回忆一些片段,如在眼前,但分明过去了很远。比如玲珊说要买房子,原来是两个月前的事了。比如阿贵上一次来...
    更向远行阅读 216评论 0 1