能让编译速度提示50%且不是通过组件二进制化实现,我们日常的提升编译速度就是将组件编译成二进制文件导入项目。本着不清楚的就去了解的原则,就来看看怎么实现的。
在ViewController中引入了Person的头文件
在我们引入头文件的时候,引入的是头文件的名称Person,那么Xcode是怎么找到这个Person文件实际位置的呢?这就要提到项目中配置的header search path
Xcode在编译的时候会读取到header search path的地址,并且拼接上我们引入的头文件名。
也就意味着我们导入的头文件分成两个部分:
前半部分:头文件所在的文件目录后半部分:头文件名称只需要设置头文件所在目录就可以了。
问题:因为我们项目里有很多文件,那么我们就会在header search path设置很多目录,但是对于找到我们上面引入一个头文件Person,他需要查找遍历所有的文件目录,来找到这个类。这个过程随着项目的类越来越多,查找的时间就会越来越长,就会越来越耗时。比如我们项目组件多达上百个,类有上万个,那么这个过程所产生的的耗时就比较明显了。
使用hmap
就是Header Map的实体,类似于一个Key-Value的形式,Key值是头文件的名称,Value是头文件的实际物理路径,其实这个东西一直都存在,只不过我们没注意到罢了。
第一次运行项目或者编译的时候,会发现很慢,但是一旦运行或者编译成功后,再次编译或者运行就会很快,想过为什么没?第一次编译后,Xcode就会帮我们生成一些.hmap文件,再次编译时候会直接使用这些.hmap文件快速找到对应的头文件,所以编译速度就会快很多
我们看到通过上面的讲解我们知道生成了很多.hmap文件,Xcode是按类别生成的,箭头指的就是我们主项目工程的.hmap文件,如果我们对Xcode进行清理,那么这些.hmap文件也会被清掉,然后我们就会发现,编译又慢了起来。
.hmap其实就是个容器,它内部肯定包含了Person的文件目录,那么就会让我们Xcode在查找Person的头文件时更快速,那么有个问题就出来了,我们自己怎么去生成.hmap文件呢?.hmap的底层结构又是怎样的呢?
我用红[]括住,我们可以看到它是用-I参数去引入了一个.hmap文件,上面我们也知道Xcode会生成多个.hmap,为了方便大家理解我们需要读取下.hmap文件
我们再看下这个项目生成的.hmap是什么文件格式
我们发现这个里面包含了项目里所有的.h,下面我们来看看.hmap究竟是什么样的数据结构
我们看到有个通过上面我们可以猜测一下.hmap的结构结构体叫HMapHeader,还有个结构体叫HMapBucket,红框有两句话:1.有一个NumBuckets的HMapBucket对象数组紧跟在这个头文件后面。2.有个字符串跟随在HMapBucket后面,在StringsOffset
最上面的HMapHeader,记录一些必要信息中间的HMapBucket,有多少个头文件,就会有多少个HMapBucket,这些都会包装成HMapBucket字符串里就是包含着头文件的前半部分路径以及后半部分类名的字符串流程:通过读取HMapHeader,获取.hmap保存了多少个Bucket,也就知道了这个.hmap保存了多少个头文件路径,而Bucket里保存了这个头文件在下面字符串中的偏移量,然后就可以从最下面的字符串中读取到该头文件的路径
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
从LLVM中我们找到hmap的有关结构信息,那么在LLVM里面是否有存取相关内容呢?
结构体信息是在Lex文件下找到,那么读取信息是不是也在Lex中HeaderMapTest的文件,感觉是测试HeaderMap的文件
我们在下面我们就来用LLVM获取的信息,写一个读取hmap时,需要用到上面的结构体
读取HeaderMap的插件(我们在main文件中写)
HMAP_HeaderMagicNumber是字符串翻转,因为在HMapHeader结构体中有个属性Magic来表示字节顺序,也就是说如果当前的Magic=HMAP_SwappedMagic,也就意味着字节顺序是反转的,也就需要重新交换下字节顺序
当参数小于两个的时候(说明没有传什么东西)这个时候就认为是无效的
循环通过dump方法导出header map
使用C来写的,因为感觉C在处理取文件时更方便些
传进来的是文件路径
解析路径
解析的路径长度小于0说明路径不正常
获取MapHeader大小并判断
拿到MapHeader大小,如果<0则说明MapHeader异常,如果小于实际的MapHeader大小,则说明读取的数据异常
判断字符串是否翻转,读取header
获取桶的数目
获取桶的数组(指针偏移)
获取String列表(指针偏移)
遍历获取桶,然后取出桶的前缀和后缀进行拼接
上面我们就把一个读取.hmap的代码写好了,下面将之前的项目的.hmap代码放到这个项目目录里,然后在下图进行设置
第一个是当前可执行文件的路径,第二个是刚才配置的.hmap路径
打印是16个桶,但是不都是头文件地址(由于数据对齐的原因)
String表有9个数据,bucket数目有16个
.hmap是一个key-value形式,key是头文件名prefix保存的是头文件路径的前半部分suffix保存的是头文件路径的后半部分(头文件名).hmap是按照对应规则存储的一堆头文件工具添加到我们的lldb执行命令里,这样我们就不用上面的方式读取.hmap文件,我们就可以在终端使用命令一样读取
xcode自己就能主动帮我们生成.hmap文件,那为什么还需要我们自己写呢?美团的文章里说了,这里我再简单的说下:
通过cocoaPods来管理第三方,比如我之前没事写的Swift项目引入下面的第三方库
以#import "ClassA.h"形式的头文件,才会命中.hmap文件,否则都将通过Header Search Path寻找其相关路径
目录的问题上面说过它会在多个目录里查找一个头文件是比较耗时,那么如果我把一个文件路径放到一个.hmap文件中,那就回快很多。此时如果引入的组件和第三方比较多,那么势必会导致编译速度慢。
LLVM中的HeaderMapTest.cpp文件,仔细看了下代码,发现里面有些生成.hmap代码,自己写的代码比较的简单,就是为了说明.hmap是如何生成的
.hmap文件说到,里面包含很多的Bucket,所以我们要先生成Bucket
创建MapFile容器Maker,Maker中包含一个个MapFile,也就是Bucket,MapFile是一个结构体(HeaderMapTest.cpp中一样,其中的8代表多少个Bucket,750是生成buffer的大小)
类名和路径以Bucket的形式保存
上面的方法都是从LLVM的HeaderMapTest.cpp中找到的
生成了一个TestApp.hmap文件,下面我们来读取下这个文件,看看和Xcode生成的是否一样
和Xcode生成的.hmap
我们发现生成的结果是一样的,下面我们就去使用下这个自己生成.hmap
将Use Header Maps设置为NO,将Header Search Paths路径设置成我们生成的.hmap路径。由于写的项目工程太小,测不出来太大的差别。
美团文章解决编译速度的思路值得我们去学习,我上面生成.hmap的方法其实无法落地的,就是为了给大家说一下怎么去生成一个.hmap,美团文章里说的cocoapods-hmap-prebuilt这个插件,我个人感觉是一个脚本,遍历头文件脚本。上面说的生成.hmap方法无法落地,如果让它能够落地,就是写一个脚本去遍历项目以及cocoapods管理的第三方库的头文件,将头文件提取出来,用上面的方法,最后生成一个.hmap文件,这样才能落地。这部分也作为自己的一个技术探索吧,后面有了结果再给大家分享
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下
我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的
我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的rubyyaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir