自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一款可以讓大型iOS工程編譯速度提升50%的工具

開發(fā) 開發(fā)工具
cocoapods-hmap-prebuilt 是美團(tuán)平臺(tái)迭代組自研的一款 cocoapods 插件,以 Header Map 技術(shù) 為基礎(chǔ),進(jìn)一步提升代碼的編譯速度,完善頭文件的搜索機(jī)制。

 

cocoapods-hmap-prebuilt 是什么?

cocoapods-hmap-prebuilt 是美團(tuán)平臺(tái)迭代組自研的一款 cocoapods 插件,以 Header Map 技術(shù) 為基礎(chǔ),進(jìn)一步提升代碼的編譯速度,完善頭文件的搜索機(jī)制。

雖然以二進(jìn)制組件的方式構(gòu)建 App 是 HPX (美團(tuán)移動(dòng)端統(tǒng)一持續(xù)集成/交付平臺(tái))的主流解決方案,但在某些場景下(Profile、Address/Thread/UB/Coverage Sanitizer、App 級(jí)別靜態(tài)檢查、ObjC 方法調(diào)用兼容性檢查等等),我們的構(gòu)建工作還是需要以全源碼編譯的方式進(jìn)行;而且在實(shí)際開發(fā)過程中,大多是以源碼的方式進(jìn)行開發(fā),所以我們將實(shí)驗(yàn)對(duì)象設(shè)置為基于全源碼編譯的流程。

廢話不多說,我們來看看它的實(shí)際使用效果!

總的來說,以美團(tuán)和大眾點(diǎn)評(píng)的全源碼編譯流程為實(shí)驗(yàn)對(duì)象的前提下,cocoapods-hmap-prebuilt 插件能將總鏈路提升 45% 以上的速度,在 Xcode 打包環(huán)節(jié)上能提升 50% 以上的速度,是不是有點(diǎn)動(dòng)心了?

為了更好的理解這個(gè)插件的價(jià)值和功能,我們不妨先看一下當(dāng)前的工程中存在的問題。

為什么現(xiàn)有的項(xiàng)目不夠好?

目前,美團(tuán)內(nèi)的 App 都是基于 CocoaPods 做包管理方面的工作,所以在實(shí)際的開發(fā)過程中,CocoaPods 會(huì)在 Pods/Header/ 目錄下添加組件名目錄和頭文件軟鏈,類似于下面的形式:

  1. /Users/sketchk/Desktop/MyApp/Pods 
  2. └── Headers 
  3.     ├── Private 
  4.     │   └── AFNetworking 
  5.     │       ├── AFHTTPRequestOperation.h -> ./XXX/AFHTTPRequestOperation.h 
  6.     │       ├── AFHTTPRequestOperationManager.h -> ./XXX/AFHTTPRequestOperationManager.h 
  7.     │       ├── ... 
  8.     │       └── UIRefreshControl+AFNetworking.h -> ./XXX/UIRefreshControl+AFNetworking.h 
  9.     └── Public 
  10.         └── AFNetworking 
  11.             ├── AFHTTPRequestOperation.h -> ./XXX/AFHTTPRequestOperation.h 
  12.             ├── AFHTTPRequestOperationManager.h -> ./XXX/AFHTTPRequestOperationManager.h 
  13.             ├── ... 
  14.             └── UIRefreshControl+AFNetworking.h -> ./XXX/UIRefreshControl+AFNetworking.h 

也正是通過這樣的目錄結(jié)構(gòu)和軟鏈,CocoaPods 得以在 Header Search Path 中添加如下的參數(shù),使得預(yù)編譯環(huán)節(jié)順利進(jìn)行。

  1. $(inherited) 
  2. ${PODS_ROOT}/Headers/Private 
  3. ${PODS_ROOT}/Headers/Private/AFNetworking 
  4. ${PODS_ROOT}/Headers/Public 
  5. ${PODS_ROOT}/Headers/Public/AFNetworking 

雖然這種構(gòu)建 Search Path 的方式解決了預(yù)編譯的問題,但在某些項(xiàng)目中,例如多達(dá) 400+ 組件的巨型項(xiàng)目中,會(huì)造成以下幾點(diǎn)問題:

  1. -I 
  2. ${PODS_ROOT}/Headers/Private 

想解決上述的問題,好一點(diǎn)的情況下,可能會(huì)浪費(fèi) 1 個(gè)小時(shí),而不好的情況,就是讓有風(fēng)險(xiǎn)的代碼上線了,你說工程師會(huì)不會(huì)因此而感到頭疼?

Header Map 是個(gè)啥?

還好 cocoapods-hmap-prebuilt 的出現(xiàn),讓這些問題變成了歷史,不過要想理解它為什么能解決這些問題,我們得先理解一下什么是 Header Map。

Header Map 其實(shí)是一組頭文件信息映射表!

為了更直觀的理解 Header Map,我們可以在 Build Setting 中開啟 Use Header Map 選項(xiàng),真實(shí)的體驗(yàn)一下它。

然后在 Build Log 里獲取相應(yīng)組件里對(duì)應(yīng)文件的編譯命令,并在最后加上 -v 參數(shù),來查看其運(yùn)行的秘密:

  1. $ clang <list of arguments> -c some-file.m -o some-file.o -v 

在 console 的輸出內(nèi)容中,我們會(huì)發(fā)現(xiàn)一段有意思的內(nèi)容:

通過上面的圖,我們可以看到編譯器將尋找頭文件的順序和對(duì)應(yīng)路徑展示出來了,而在這些路徑中,我們看到了一些陌生的東西,即后綴名為 .hmap 的文件,后面還有個(gè)括號(hào)寫著 headermap。

沒錯(cuò)!它就是 Header Map 的實(shí)體。

此時(shí) Clang 已經(jīng)在剛才提到的 hmap 文件里塞入了一份頭文件名和頭文件路徑的映射表,不過它是一種二進(jìn)制格式的文件,為了驗(yàn)證這個(gè)的說法,我們可以通過 milend 編寫的 hmap 工具 來查其內(nèi)容。

在執(zhí)行相關(guān)命令(即 hmap print )后,我們可以發(fā)現(xiàn)這些 hmap 里保存的信息結(jié)構(gòu)大致如下, 類似于一個(gè) Key-Value 的形式,Key 值是頭文件的名稱,Value 是頭文件的實(shí)際物理路徑:

需要注意,映射表的鍵值內(nèi)容會(huì)隨著使用場景產(chǎn)生不同的變化,例如頭文件引用是在 "..." 的形式下,還是 <...> 的形式下,又或是在 Build Phase 里 Header 的配置情況。例如,你將頭文件設(shè)置為 Public 的時(shí)候,在某些 hmap 中,它的 Key 值就為 PodA/ClassA ,而將其設(shè)置為 project 的時(shí)候,它的 Key 值可能就是 ClassA ,而配置這些信息的地方,如下圖所示:

至此我想你應(yīng)該了解到 Header Map 到底是個(gè)什么東西了。

當(dāng)然這種技術(shù)也不是一個(gè)什么新鮮事兒,在 Facebook 的buck工具中也提供了類似的東西,只不過文件類型變成了 HeaderMap.java 的樣子。

此時(shí),我估計(jì)你可能并不會(huì)對(duì) buck 產(chǎn)生太多的興趣,而是開始思考上一張圖中 Headers 的 Public、Private、Project 到底代表著什么意思,好像很多同學(xué)從來沒怎么關(guān)注過,以及為什么它會(huì)影響 hmap 里的內(nèi)容?

Public,Private,Project 是個(gè)啥?

在 Apple 官方的 Xcode Help - What are build phases? 文檔中,我們可以看到如下的一段解釋:

  1. Associates publicprivate, or project header files with the target. Public and private headers define API intended for use by other clients, and are copied into a product for installation. For example, public and private headers in a framework target are copied into Headers and PrivateHeaders subfolders within a product. Project headers define API used and built by a target, but not copied into a product. This phase can be used once per target. 

總的來說,我們可以知道一點(diǎn),就是 Build Phases - Headers 中提到 Public 和 Private 是指可以供外界使用的頭文件,而 Project 中的頭文件是不對(duì)外使用的,也不會(huì)放在最終的產(chǎn)物中。

如果你繼續(xù)翻閱一些資料,例如 StackOverflow - Xcode: Copy Headers: Public vs. Private vs. Project? 和 StackOverflow - Understanding Xcode’s Copy Headers phase ,你會(huì)發(fā)現(xiàn)在早期 Xcode Help 的 Project Editor 章節(jié)里,有一段名為 Setting the Role of a Header File 的段落,里面詳細(xì)記載了三個(gè)類型的區(qū)別。

Public: The interface is finalized and meant to be used by your product’s clients. A public header is included in the product as readable source code without restriction. Private : The interface isn’t intended for your clients or it’s in early stages of development. A private header is included in the product, but it’s marked “private”. Thus the symbols are visible to all clients, but clients should understand that they’re not supposed to use them. Project : The interface is for use only by implementation files in the current project. A project header is not included in the target, except in object code. The symbols are not visible to clients at all, only to you.

至此,我們應(yīng)該能夠徹底了解了 Public、Private、Project 的區(qū)別。簡而言之,Public 還是通常意義上的 Public,Private 則代表 In Progress 的含義,至于 Project 才是通常意義上的 Private 含義。

此時(shí),你會(huì)不會(huì)聯(lián)想到 CocoaPods 中 Podspec 的 Syntax 里還有 public_header_files 和 private_header_files 兩個(gè)字段,它們的真實(shí)含義是否和 Xcode 里的概念沖突呢?

這里我們仔細(xì)閱讀一下 官方文檔的解釋 ,尤其是 private_header_files 字段。

我們可以看到, private_header_files 在這里的含義是說,它本身是相對(duì)于 Public 而言的,這些頭文件本義是不希望暴露給用戶使用的,而且也不會(huì)產(chǎn)生相關(guān)文檔,但是在構(gòu)建的時(shí)候,會(huì)出現(xiàn)在最終產(chǎn)物中,只有既沒有被 Public 和 Private 標(biāo)注的頭文件,才會(huì)被認(rèn)為是真正的私有頭文件,且不出現(xiàn)在最終的產(chǎn)物里。

看起來,CocoaPods 對(duì)于 Public 和 Private 的官方解釋是和 Xcode 中的描述一致的,兩處的 Private 并非我們通常理解的 Private,它的本意更應(yīng)該是開發(fā)者準(zhǔn)備對(duì)外開放,但又沒完全 Ready 的頭文件,更像一個(gè) In Progress 的含義。

這一塊是不是讓你有點(diǎn)大跌眼鏡?那么,在現(xiàn)實(shí)世界中,我們是否正確的使用了它們呢?

為什么用原生的 hmap 不能改善編譯速度?

前面我們介紹了 hmap 是什么,以及怎么開啟它(啟用 Build Setting 中的 Use Header Map 選項(xiàng)),也介紹了一些影響生成 hmap 的因素(Public、Private、Project)。

那是不是我只要開啟 Xcode 提供的 Use Header Map 就可以提升編譯速度了呢?

很可惜,答案是否定的!

至于原因,我們就從下面的例子開始說起,假設(shè)我們有一個(gè)基于 CocoaPods 構(gòu)建的全源碼工程項(xiàng)目,它的整體結(jié)構(gòu)如下:

  • 首先,Host 和 Pod 是我們的兩個(gè) Project,Pods 下的 Target 的產(chǎn)物類型為 Static Library。
  • 其次,Host 底下會(huì)有一個(gè)同名的 Target,而 Pods 目錄下會(huì)有 n+1 個(gè) Target,其中 n 取決于你依賴的組件數(shù)量,而 1 是一個(gè)名為 Pods-XXX 的 Target,最后,Pods-XXX 這個(gè) Target 的產(chǎn)物會(huì)被 Host 里的 Target 所依賴。

整個(gè)結(jié)構(gòu)看起來如下所示:

當(dāng)構(gòu)建的產(chǎn)物類型為 Static Library 的時(shí)候,CocoaPods 在創(chuàng)建頭文件產(chǎn)物過程中,它的邏輯大致如下:

  • 不論 podspec 里如何設(shè)置 public_header_files 和 private_header_files ,相應(yīng)的頭文件都會(huì)被設(shè)置為 Project 類型。
  • 在 Pods/Headers/Public 中會(huì)保存所有被聲明為 public_header_files 的頭文件。
  • 在 Pods/Headers/Private 中會(huì)保存所有頭文件,不論是 public_header_files 或者 private_header_files 描述到,還是那些未被描述的,這個(gè)目錄下是當(dāng)前組件的所有頭文件全集。
  • 如果 podspec 里未標(biāo)注 Public 和 Private 的時(shí)候, Pods/Headers/Public 和 Pods/Headers/Private 的內(nèi)容一樣且會(huì)包含所有頭文件。

正是由于這種機(jī)制,會(huì)導(dǎo)致一些有意思的問題發(fā)生。

  • 首先,由于所有頭文件都被當(dāng)做最終產(chǎn)物保留下來,在結(jié)合 Header Search Path 里 Pods/Headers/Private 路徑的存在,我們完全可以引用到其他組件里的私有頭文件,例如我只要使用 #import <SomePod/Private_Header.h> 的方式,就會(huì)命中私有文件的匹配路徑。
  • 其次,就是在 Static Library 的狀況下,一旦我們開啟了 Use Header Map,結(jié)合組件里所有頭文件的類型為 Project 的情況,這個(gè) hmap 里只會(huì)包含 #import "ClassA.h" 的鍵值引用,也就是說只有 #import "ClassA.h" 的方式才會(huì)命中 hmap 的策略,否則都將通過 Header Search Path 尋找其相關(guān)路徑,例如下圖中的 PodB,在其 build 的過程中,Xcode 會(huì)為 PodB 生成 5 個(gè) hmap 文件,也就是說這 5 個(gè)文件只會(huì)在編譯 PodB 中使用,其中 PodB 會(huì)依賴 PodA 的一些頭文件,但由于 PodA 中的頭文件都是 Project 類型的,所以其在 hmap 里的 Key 全部為 ClassA.h ,也就是說我們只能以 #import "ClassA.h" 的方式引入。

而我們也知道,在引用其他組件的時(shí)候,通常都會(huì)采用 #import 的方式引入。至于為什么會(huì)用這種方式,一方面是這種寫法會(huì)明確頭文件的由來,避免問題,另一方面也是這種方式可以讓我們?cè)谑欠耖_啟 clang module 中隨意切換。當(dāng)然,還有一點(diǎn)就是Apple 在 WWDC 里曾經(jīng)不止一次建議開發(fā)者使用這種方式來引入頭文件。

接著上面的話題來說,所以說在 Static Library 的情況下且以 #import <A/A.h> 這種標(biāo)準(zhǔn)方式引入頭文件時(shí),開啟 Use Header Map 選項(xiàng)并不會(huì)幫我們提升編譯速度。

但真的就沒有辦法使用 Header Map 了么?

cocoapods-hmap-prebuilt 誕生了

當(dāng)然,總是有辦法解決的,我們完全可以自己動(dòng)手做一個(gè)基于 CocoaPods 規(guī)則下的 hmap 文件,正是基于這個(gè)想法,美團(tuán)自研的 cocoapods-hmap-prebuilt 插件誕生了!

它的核心功能并不多,大概有以下幾點(diǎn):

組件名/頭文件名

聽起來可能有點(diǎn)繞,內(nèi)容也有點(diǎn)多,不過這些你都不用關(guān)心,你只需要通過以下 2 個(gè)步驟就能將其使用起來:

  1. 在 Gemfile 里聲明插件。
  2. 在 Podfile 里使用插件。
  1. // this is part of Gemfile 
  2. source 'http://sakgems.sankuai.com/' do 
  3.   gem 'cocoapods-hmap-prebuilt' 
  4.   gem 'XXX' 
  5.   ... 
  6. end 
  7.  
  8. // this is part of Podfile 
  9. target 'XXX' do 
  10.   plugin 'cocoapods-hmap-prebuilt' 
  11.   pod 'XXX' 
  12.   ... 
  13. end 

除此之外,為了拓展其實(shí)用性,我們還提供了頭文件補(bǔ)?。ń鉀Q重名頭文件的定向選取)和環(huán)境變量注入(無侵入的在其他系統(tǒng)中使用)的能力,便于其在不同場景下的使用。

總結(jié)

至此,關(guān)于 cocoapods-hmap-prebuilt 的介紹就要結(jié)束了。

回看整個(gè)故事的開始,Header Map 是我在研究 Swift 和 Objective-C 混編過程中發(fā)現(xiàn)的一個(gè)很小的知識(shí)點(diǎn),而且 Xcode 自身就實(shí)現(xiàn)了一套基于 Header Map 的功能,在實(shí)際的使用過程中,它的表現(xiàn)并不理想。

但幸運(yùn)的是,在后續(xù)的探索的過程中,我們發(fā)現(xiàn)了為什么 Xcode 的 Header Map 沒有生效,以及為什么它與 CocoaPods 出現(xiàn)了不兼容的情況,雖然它的原理并不復(fù)雜,核心點(diǎn)就是將文件查找和讀取等 IO 操作編變成了內(nèi)存讀取操作,但結(jié)合實(shí)際的業(yè)務(wù)場景,我們發(fā)現(xiàn)它的收益是十分可觀的。

或許這是在提醒我們,要永遠(yuǎn)對(duì)技術(shù)保持一顆好奇的心!

其實(shí),利用 Clang Module 技術(shù)也可以解決本文一開始提到的幾個(gè)問題,但它并不在這篇文章的討論范圍中,如果你對(duì) Clang Module 或者對(duì) Swift 與 Objective-C 混編感興趣,歡迎閱讀參考文檔中的 《從預(yù)編譯的角度理解 Swift 與 Objective-C 及混編機(jī)制》一文,以了解更多的詳細(xì)信息。

 

 

責(zé)任編輯:張燕妮 來源: 美團(tuán)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2020-07-09 10:02:27

Python開發(fā)工具

2015-09-23 17:39:52

Github開源工具

2015-09-28 09:56:17

Github開源工具編程

2021-11-01 05:53:08

Doldrums逆向工程分析工具安全工具

2025-03-10 00:00:50

2019-03-28 14:22:26

工具代碼開發(fā)

2025-04-10 09:10:00

.NET開源Windows

2021-01-27 13:16:39

ScreenLinux命令

2021-02-16 10:58:50

ScreenLinux命令

2014-06-20 10:32:42

APP上癮設(shè)計(jì)

2021-07-09 10:14:05

IP工具命令

2013-10-15 09:26:12

2020-12-22 10:30:47

Nagios工具監(jiān)控

2011-05-10 09:55:14

2025-01-22 16:13:07

2024-02-23 08:13:25

Excalidraw白板工具開源

2011-01-11 13:45:20

2025-04-07 08:10:00

2021-03-25 16:15:24

SQL工具慢查詢

2020-05-28 09:33:07

Web調(diào)試代理工具Fiddler
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)