CocoaPods 是開發(fā) OS X 和 iOS 應(yīng)用程序的一個第三方庫的依賴管理工具。利用 CocoaPods,可以定義自己的依賴關(guān)系 (稱作pods),并且隨著時間的變化,以及在整個開發(fā)環(huán)境中對第三方庫的版本管理非常方便。
CocoaPods 背后的理念主要體現(xiàn)在兩個方面。首先,在工程中引入第三方代碼會涉及到許多內(nèi)容。針對 Objective-C 初級開發(fā)者來說,工程文件的配置會讓人很沮喪。在配置 build phases 和 linker flags 過程中,會引起許多人為因素的錯誤。CocoaPods 簡化了這一切,它能夠自動配置編譯選項。
其次,通過 CocoaPods,可以很方便的查找到新的第三方庫。當(dāng)然,這并不是說你可以簡單的將別人提供的庫拿來拼湊成一個應(yīng)用程序。它的真正作用是讓你能夠找到真正好用的庫,以此來縮短我們的開發(fā)周期和提升軟件的質(zhì)量。
本文中,我們將通過分析pod 安裝 (pod install)的過程,一步一步揭示 CocoaPods 背后的技術(shù)。
核心組件
CocoaPods是用 Ruby 寫的,并由若干個 Ruby 包 (gems) 構(gòu)成的。在解析整合過程中,最重要的幾個 gems 分別是:CocoaPods/CocoaPods,CocoaPods/Core, 和CocoaPods/Xcodeproj(是的,CocoaPods 是一個依賴管理工具 -- 利用依賴管理進行構(gòu)建的!)。
編者注CocoaPods 是一個 objc 的依賴管理工具,而其本身是利用 ruby 的依賴管理 gem 進行構(gòu)建的
CocoaPods/CocoaPod
這是是一個面向用戶的組件,每當(dāng)執(zhí)行一個pod命令時,這個組件都將被激活。該組件包括了所有使用 CocoaPods 涉及到的功能,并且還能通過調(diào)用所有其它的 gems 來執(zhí)行任務(wù)。
CocoaPods/Core
Core 組件提供支持與 CocoaPods 相關(guān)文件的處理,文件主要是 Podfile 和 podspecs。
Podfile
Podfile 是一個文件,用于定義項目所需要使用的第三方庫。該文件支持高度定制,你可以根據(jù)個人喜好對其做出定制。更多相關(guān)信息,請查閱Podfile 指南。
Podspec
.podspec也是一個文件,該文件描述了一個庫是怎樣被添加到工程中的。它支持的功能有:列出源文件、framework、編譯選項和某個庫所需要的依賴等。
CocoaPods/Xcodeproj
這個 gem 組件負責(zé)所有工程文件的整合。它能夠?qū)?chuàng)建并修改.xcodeproj和.xcworkspace文件。它也可以作為單獨的一個 gem 包使用。如果你想要寫一個腳本來方便的修改工程文件,那么可以使用這個 gem。
運行pod install命令
當(dāng)運行pod install命令時會引發(fā)許多操作。要想深入了解這個命令執(zhí)行的詳細內(nèi)容,可以在這個命令后面加上--verbose?,F(xiàn)在運行這個命令pod install --verbose,可以看到類似如下的內(nèi)容:

可以上到,整個過程執(zhí)行了很多操作,不過把它們分解之后,再看看,會發(fā)現(xiàn)它們都很簡單。讓我們逐步來分析一下。
讀取 Podfile 文件
你是否對 Podfile 的語法格式感到奇怪過,那是因為這是用 Ruby 語言寫的。相較而言,這要比現(xiàn)有的其他格式更加簡單好用一些。
在安裝期間,第一步是要弄清楚顯示或隱式的聲明了哪些第三方庫。在加載 podspecs 過程中,CocoaPods 就建立了包括版本信息在內(nèi)的所有的第三方庫的列表。Podspecs 被存儲在本地路徑~/.cocoapods中。
版本控制和沖突
CocoaPods 使用語義版本控制 - Semantic Versioning命名約定來解決對版本的依賴。由于沖突解決系統(tǒng)建立在非重大變更的補丁版本之間,這使得解決依賴關(guān)系變得容易很多。例如,兩個不同的 pods 依賴于 CocoaLumberjack 的兩個版本,假設(shè)一個依賴于2.3.1,另一個依賴于2.3.3,此時沖突解決系統(tǒng)可以使用最新的版本2.3.3,因為這個可以向后與2.3.1兼容。
但這并不總是有效。有許多第三方庫并不使用這樣的約定,這讓解決方案變得非常復(fù)雜。
當(dāng)然,總會有一些沖突需要手動解決。如果一個庫依賴于 CocoaLumberjack 的1.2.5,另外一個庫則依賴于2.3.1,那么只有最終用戶通過明確指定使用某個版本來解決沖突。
加載源文件
CocoaPods 執(zhí)行的下一步是加載源碼。每個.podspec文件都包含一個源代碼的索引,這些索引一般包裹一個 git 地址和 git tag。它們以 commit SHAs 的方式存儲在~/Library/Caches/CocoaPods中。這個路徑中文件的創(chuàng)建是由 Core gem 負責(zé)的。
CocoaPods 將依照Podfile、.podspec和緩存文件的信息將源文件下載到Pods目錄中。
生成 Pods.xcodeproj
每次pod install執(zhí)行,如果檢測到改動時,CocoaPods 會利用 Xcodeproj gem 組件對Pods.xcodeproj進行更新。如果該文件不存在,則用默認配置生成。否則,會將已有的配置項加載至內(nèi)存中。
安裝第三方庫
當(dāng) CocoaPods 往工程中添加一個第三方庫時,不僅僅是添加代碼這么簡單,還會添加很多內(nèi)容。由于每個第三方庫有不同的 target,因此對于每個庫,都會有幾個文件需要添加,每個 target 都需要:
一個包含編譯選項的.xcconfig文件
一個同時包含編譯設(shè)置和 CocoaPods 默認配置的私有.xcconfig文件
一個編譯所必須的prefix.pch文件
另一個編譯必須的文件dummy.m
一旦每個 pod 的 target 完成了上面的內(nèi)容,整個Podstarget 就會被創(chuàng)建。這增加了相同文件的同時,還增加了另外幾個文件。如果源碼中包含有資源 bundle,將這個 bundle 添加至程序 target 的指令將被添加到Pods-Resources.sh文件中。還有一個名為Pods-environment.h的文件,文件中包含了一些宏,這些宏可以用來檢查某個組件是否來自 pod。最后,將生成兩個認可文件,一個是plist,另一個是markdown,這兩個文件用于給最終用戶查閱相關(guān)許可信息。
寫入至磁盤
直到現(xiàn)在,許多工作都是在內(nèi)存中進行的。為了讓這些成果能被重復(fù)利用,我們需要將所有的結(jié)果保存到一個文件中。所以Pods.xcodeproj文件被寫入磁盤,另外兩個非常重要的文件:Podfile.lock和Manifest.lock都將被寫入磁盤。
Podfile.lock
這是 CocoaPods 創(chuàng)建的最重要的文件之一。它記錄了需要被安裝的 pod 的每個已安裝的版本。如果你想知道已安裝的 pod 是哪個版本,可以查看這個文件。推薦將 Podfile.lock 文件加入到版本控制中,這有助于整個團隊的一致性。
Manifest.lock
這是每次運行pod install命令時創(chuàng)建的Podfile.lock文件的副本。如果你遇見過這樣的錯誤沙盒文件與 Podfile.lock 文件不同步 (The sandbox is not in sync with the Podfile.lock),這是因為 Manifest.lock 文件和Podfile.lock文件不一致所引起。由于Pods所在的目錄并不總在版本控制之下,這樣可以保證開發(fā)者運行 app 之前都能更新他們的 pods,否則 app 可能會 crash,或者在一些不太明顯的地方編譯失敗。
xcproj
如果你已經(jīng)依照我們的建議在系統(tǒng)上安裝了xcproj,它會對Pods.xcodeproj文件執(zhí)行一下touch以將其轉(zhuǎn)換成為舊的 ASCII plist 格式的文件。為什么要這么做呢?雖然在很久以前就不被其它軟件支持了,但是 Xcode 仍然依賴于這種格式。如果沒有 xcproj,你的Pods.xcodeproj文件將會以 XML 格式的 plist 文件存儲,當(dāng)你用 Xcode 打開它時,它會被改寫,并造成大量的文件改動。
結(jié)果
運行pod install命令的最終結(jié)果是許多文件被添加到你的工程和系統(tǒng)中。這個過程通常只需要幾秒鐘。當(dāng)然沒有 Cocoapods 這些事也都可以完成。只不過所花的時間就不僅僅是幾秒而已了。
補充:持續(xù)集成
CocoaPods 和持續(xù)集成在一起非常融洽。雖然持續(xù)集成很大程度上取決于你的項目配置,但 Cocoapods 依然能很容易地對項目進行編譯。
Pods 文件夾的版本控制
如果 Pods 文件夾和里面的所有內(nèi)容都在版本控制之中,那么你不需要做什么特別的工作,就能夠持續(xù)集成。我們只需要給.xcworkspace選擇一個正確的 scheme 即可。
不受版本控制的 Pods 文件夾
如果你的Pods文件夾不受版本控制,那么你需要做一些額外的步驟來保證持續(xù)集成的順利進行。最起碼,Podfile文件要放入版本控制之中。另外強烈建議將生成的.xcworkspace和Podfile.lock文件納入版本控制,這樣不僅簡單方便,也能保證所使用 Pod 的版本是正確的。
一旦配置完畢,在持續(xù)集成中運行 CocoaPods 的關(guān)鍵就是確保每次編譯之前都執(zhí)行了pod install命令。在大多數(shù)系統(tǒng)中,例如 Jenkins 或 Travis,只需要定義一個編譯步驟即可 (實際上,Travis 會自動執(zhí)行pod install命令)。對于Xcode Bots,在書寫這篇文章時我們還沒能找到非常流暢的方式,不過我們正朝著解決方案努力,一旦成功,我們將會立即分享。
結(jié)束語
CocoaPods 簡化了 Objective-C 的開發(fā)流程,我們的目標是讓第三方庫更容易被發(fā)現(xiàn)和添加。了解 CocoaPods 的原理能讓你做出更好的應(yīng)用程序。我們沿著 CocoaPods 的整個執(zhí)行過程,從載入 specs 文件和源代碼、創(chuàng)建.xcodeproj文件和所有組件,到將所有文件寫入磁盤。所以接下來,我們運行pod install --verbose,靜靜觀察 CocoaPods 的魔力如何顯現(xiàn)。