推广

由美团文章“一款可以让大型iOS工程编译速度提升50%的工具”引出的.hmap文件探索(上)

iseeyu3年前 (2024-02-22)推广162

前言

前段时间,本鑫看了一篇美团的文章:《一款可以让大型iOS工程编译速度提升50%的工具》,一看标题就觉得惊讶,为什么呢?因为它能让编译速度提示50%且不是通过组件二进制化实现,我们日常的提升编译速度就是将组件编译成二进制文件导入项目。本着不清楚的就去了解的原则,就来看看怎么实现的。

探索

编译耗时原因

在项目中我们会引入头文件,例如下图:我们在ViewController中引入了Person的头文件

在我们引入头文件的时候,引入的是头文件的名称Person,那么Xcode是怎么找到这个Person文件实际位置的呢?这就要提到项目中配置的header search path

Xcode在编译的时候会读取到header search path的地址,并且拼接上我们引入的头文件名。

也就意味着我们导入的头文件分成两个部分:

  • 1.前半部分:头文件所在的文件目录
  • 2.后半部分:头文件名称

这也就是为什么我们设置header search path的时候,只需要设置头文件所在目录就可以了。

问题:因为我们项目里有很多文件,那么我们就会在header search path设置很多目录,但是对于找到我们上面引入一个头文件Person,他需要查找遍历所有的文件目录,来找到这个类。这个过程随着项目的类越来越多,查找的时间就会越来越长,就会越来越耗时。比如我们项目组件多达上百个,类有上万个,那么这个过程所产生的的耗时就比较明显了。

解决办法

上面我们知道项目编译耗时的原因,那么怎么解决这个问题呢?美团的文章给出答案,就是使用hmap

hmap

hmap是什么呢?美团文章说了它就是Header Map的实体,类似于一个Key-Value的形式,Key值是头文件的名称,Value是头文件的实际物理路径,其实这个东西一直都存在,只不过我们没注意到罢了。

  • 大家想一下,第一次运行项目或者编译的时候,会发现很慢,但是一旦运行或者编译成功后,再次编译或者运行就会很快,想过为什么没?
  • 其实第一次编译后,Xcode就会帮我们生成一些.hmap文件,再次编译时候会直接使用这些.hmap文件快速找到对应的头文件,所以编译速度就会快很多

我们看到生成了很多.hmap文件,Xcode是按类别生成的,箭头指的就是我们主项目工程的.hmap文件,如果我们对Xcode进行清理,那么这些.hmap文件也会被清掉,然后我们就会发现,编译又慢了起来。

通过上面的讲解我们知道.hmap其实就是个容器,它内部肯定包含了Person的文件目录,那么就会让我们Xcode在查找Person的头文件时更快速,那么有个问题就出来了,我们自己怎么去生成.hmap文件呢?.hmap的底层结构又是怎样的呢?

探究.hmap文件

我们编译一个项目,查看编译过程,找到ViewController.m文件

我用红[]括住,我们可以看到它是用-I参数去引入了一个.hmap文件,上面我们也知道Xcode会生成多个.hmap,为了方便大家理解我们需要读取下.hmap文件

.hmap文件结构分析

先看下项目目录

我们再看下这个项目生成的.hmap是什么文件格式

我们发现这个里面包含了项目里所有的.h,下面我们来看看.hmap究竟是什么样的数据结构

  • 数据结构

我们可以通过LLVM来查找相关的内容

我们看到有个结构体叫HMapHeader,还有个结构体叫HMapBucket,红框有两句话:1.有一个NumBuckets的HMapBucket对象数组紧跟在这个头文件后面。2.有个字符串跟随在HMapBucket后面,在StringsOffset

通过上面我们可以猜测一下.hmap的结构

  • 1.最上面的HMapHeader,记录一些必要信息
  • 2.中间的HMapBucket,有多少个头文件,就会有多少个HMapBucket,这些都会包装成HMapBucket
  • 3.字符串里就是包含着头文件的前半部分路径以及后半部分类名的字符串

流程:通过读取HMapHeader,获取.hmap保存了多少个Bucket,也就知道了这个.hmap保存了多少个头文件路径,而Bucket里保存了这个头文件在下面字符串中的偏移量,然后就可以从最下面的字符串
中读取到该头文件的路径

首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)

读取.hmap文件

我们怎么读取.hmap信息呢?上面从LLVM中我们找到hmap的有关结构信息,那么在LLVM里面是否有存取相关内容呢?

  • 上面我们知道结构体信息是在Lex文件下找到,那么读取信息是不是也在Lex中
  • 最后我找到一个HeaderMapTest的文件,感觉是测试HeaderMap的文件

我们在读取hmap时,需要用到上面的结构体

下面我们就来用LLVM获取的信息,写一个读取HeaderMap的插件(我们在main文件中写)

hmap读取

我们在main函数中写如下代码:

  • 断言宏

HMAP_HeaderMagicNumber是字符串翻转,因为在HMapHeader结构体中有个属性Magic来表示字节顺序,也就是说如果当前的Magic=HMAP_SwappedMagic,也就意味着字节顺序是反转的,也就需要重新交换下字节顺序

  • 2.参数判断非正常文件

当参数小于两个的时候(说明没有传什么东西)这个时候就认为是无效的

  • 3.正常文件

循环通过dump方法导出header map

dump方法

这个方法我是使用C来写的,因为感觉C在处理取文件时更方便些

传进来的是文件路径

  • 1.解析路径

解析的路径长度小于0说明路径不正常

  • 2.获取MapHeader大小并判断

拿到MapHeader大小,如果<0则说明MapHeader异常,如果小于实际的MapHeader大小,则说明读取的数据异常

  • 3.判断字符串是否翻转,读取header

  • 4.获取桶的数目

  • 5.获取桶的数组(指针偏移)

  • 6.获取String列表(指针偏移)

  • 7.遍历获取桶,然后取出桶的前缀和后缀进行拼接

上面我们就把一个读取.hmap的代码写好了,下面将之前的项目的.hmap代码放到这个项目目录里,然后在下图进行设置

运行项目,打断点

  • 1.main函数断点

第一个是当前可执行文件的路径,第二个是刚才配置的.hmap路径

  • 2.查看桶数目

打印是16个桶,但是不都是头文件地址(由于数据对齐的原因)

  • 3.查看打印数据

String表有9个数据,bucket数目有16个

  • 4.查看结果

总结

通过上面的读取打印我们可以确认一下几点:

  • 1.上面说的.hmap是一个key-value形式,key是头文件名
  • 2.prefix保存的是头文件路径的前半部分
  • 3.suffix保存的是头文件路径的后半部分(头文件名)
  • 4..hmap是按照对应规则存储的一堆头文件

也证明了上面我们的猜想是对的

扩展

上面写的代码可以生成一个工具,我们把工具添加到我们的lldb执行命令里,这样我们就不用上面的方式读取.hmap文件,我们就可以在终端使用命令一样读取

生成自己的.hmap文件

上面说了xcode自己就能主动帮我们生成.hmap文件,那为什么还需要我们自己写呢?美团的文章里说了,这里我再简单的说下:

  • 1.我们的项目一般都会通过cocoaPods来管理第三方,比如我之前没事写的Swift项目引入下面的第三方库

  • 2.上面我们发现以#import "ClassA.h"形式的头文件,才会命中.hmap文件,否则都将通过Header Search Path寻找其相关路径

目录的问题上面说过它会在多个目录里查找一个头文件是比较耗时,那么如果我把一个文件路径放到一个.hmap文件中,那就回快很多。此时如果引入的组件和第三方比较多,那么势必会导致编译速度慢。

写代码生成自己的.hmap文件

这部分也是个难点,本人也是查看了上面提到的LLVM中的HeaderMapTest.cpp文件,仔细看了下代码,发现里面有些生成.hmap代码,自己写的代码比较的简单,就是为了说明.hmap是如何生成的

  • 1.上面介绍.hmap文件说到,里面包含很多的Bucket,所以我们要先生成Bucket

创建MapFile容器Maker,Maker中包含一个个MapFile,也就是Bucket,MapFile是一个结构体(HeaderMapTest.cpp中一样,其中的8代表多少个Bucket,750是生成buffer的大小)

  • 2.核心代码,将类名和路径以Bucket的形式保存
    • 方法总览

*   addString方法

*   addBucket方法

上面的方法都是从LLVM的HeaderMapTest.cpp中找到的

  • 3.将文件导出指定位置
    • 方法总览

*   getBuffer方法

  • 4.运行项目

生成了一个TestApp.hmap文件,下面我们来读取下这个文件,看看和Xcode生成的是否一样

  • 5.读取生成的TestApp.hmap

和Xcode生成的.hmap

我们发现生成的结果是一样的,下面我们就去使用下这个自己生成.hmap

  • 6.使用自己生成.hmap

将Use Header Maps设置为NO,将Header Search Paths路径设置成我们生成的.hmap路径。由于写的项目工程太小,测不出来太大的差别。

总结

上面讲了.hmap的读写方法,看完也就.hmap有个比较清晰的认识了,美团文章解决编译速度的思路值得我们去学习,我上面生成.hmap的方法其实无法落地的,就是为了给大家说一下怎么去生成一个.hmap,美团文章里说的cocoapods-hmap-prebuilt这个插件,我个人感觉是一个脚本,遍历头文件脚本。上面说的生成.hmap方法无法落地,如果让它能够落地,就是写一个脚本去遍历项目以及cocoapods管理的第三方库的头文件,将头文件提取出来,用上面的方法,最后生成一个.hmap文件,这样才能落地。这部分也作为自己的一个技术探索吧,后面有了结果再给大家分享

补充

.hmap已经落地,可以看下篇更新的文章《由美团文章“一款可以让大型iOS工程编译速度提升50%的工具”引出的.hmap文件(下)hmap落地》,文章结尾会放出来插件链接

扫描二维码推送至手机访问。

版权声明:本文由西安泽虎代运营发布,如需转载请注明出处。

转载请注明出处https://www.0291.com.cn/post/55915.html

相关文章

西安企业做百度推广,一个月花三千和花三万到底差在哪?

4|最近跟几个西安的老板聊天,发现一个挺有意思的现象:同样做百度推广,有人一个月花三千块,询盘不断;有人一个月砸三万,水花都没见着。问他们区别在哪,大多数人说不清楚,只知道'钱花了,没效果'。 5| 6|百度推广这东西,说复杂也复杂,说简单也简单。核心就一件事:让你的潜在客户搜某个词的时候,你的信息...

我想当策划,大学应该选什么专业?

我想当策划,大学应该选什么专业?

学,广告,传播,公关,这些专业其实都可以。另外也可以学像汉语言文学,以及一些跟文学相关的一些专业,会对你找工作有帮助。但是,你想从事这个这份职业,除了专业的学习之外,更多的是,你要在上学的时候积极参与一些更营销策划相关的社会实践。比如说你可以做一些兼职,比如说帮别人去写稿,...

服装市场营销策略分析,“定位不慎满盘皆输”

服装市场营销策略分析,“定位不慎满盘皆输”

不同的区域需要不同的策略,而不同的营销策略需要不同的销售思路来配合。因此,在研究女装产品销售思路之前,最好先对于市场与营销策略做一次深入地了解。1.市场区域化(market segmentation):目标营销的第一步,是将整个市场拆分成若干个同性质的小市场。2.目标市场的...

晚间悦读丨你的思维方式,决定你的人生高度!

晚间悦读丨你的思维方式,决定你的人生高度!

越长大,越发现,能够决定你人生高度的,往往是你的认知层次。 你的认知在哪个层级,你的人生就处在什么状态。你认知的高度,决定了你人生的高度。 那如何才能升级方式、提升认知能力呢?可以从这三个方面做出改变。 NO. 1 改变态度 有人...

编写项目营销策划方案六大要点

编写项目营销策划方案六大要点

现在商业发展非常迅速,的需求非常的大,但是要做好营销策划是有一定难度的,我把自己做营销策划的一些体会在这里分享给朋友们。营销策划方案六大要点我们先说项目营销策划方案这个话题。大部分的营销策划人都是在企业里工作,大部分项目之初都是来源于一个想法,创始人、老板或者创始团队的一个...

【商场营销策划】如何进行商场营销策划?

【商场营销策划】如何进行商场营销策划?

商场是策划者们关注的永恒话题,那么究竟该如何作好商场营销策划呢?掌握以下这几点营销策略设计原则,至关重要!1.义利兼顾的原则。商场在获取利润的同时要充分考虑到消费者的合法利益和商场的长远利益。在作商场活动营销策略方案的时候应该将这一点作为首要的原则。对商场有利而对顾客不利的...

现在,非常期待与您的又一次邂逅

我们努力让每一部企业宣传片和抖音短视频成为商业大片