鍍金池/ 教程/ iOS/ 深入理解 CocoaPods
與四軸無(wú)人機(jī)的通訊
在沙盒中編寫(xiě)腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開(kāi)發(fā)
Collection View 動(dòng)畫(huà)
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫(huà)
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫(huà)解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫(huà)
常見(jiàn)的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無(wú)人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無(wú)止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫(huà)
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺(jué)
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開(kāi)發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語(yǔ)言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過(guò)程

深入理解 CocoaPods

CocoaPods 是開(kāi)發(fā) OS X 和 iOS 應(yīng)用程序的一個(gè)第三方庫(kù)的依賴管理工具。利用 CocoaPods,可以定義自己的依賴關(guān)系 (稱作 pods),并且隨著時(shí)間的變化,以及在整個(gè)開(kāi)發(fā)環(huán)境中對(duì)第三方庫(kù)的版本管理非常方便。

CocoaPods 背后的理念主要體現(xiàn)在兩個(gè)方面。首先,在工程中引入第三方代碼會(huì)涉及到許多內(nèi)容。針對(duì) Objective-C 初級(jí)開(kāi)發(fā)者來(lái)說(shuō),工程文件的配置會(huì)讓人很沮喪。在配置 build phases 和 linker flags 過(guò)程中,會(huì)引起許多人為因素的錯(cuò)誤。CocoaPods 簡(jiǎn)化了這一切,它能夠自動(dòng)配置編譯選項(xiàng)。

其次,通過(guò) CocoaPods,可以很方便的查找到新的第三方庫(kù)。當(dāng)然,這并不是說(shuō)你可以簡(jiǎn)單的將別人提供的庫(kù)拿來(lái)拼湊成一個(gè)應(yīng)用程序。它的真正作用是讓你能夠找到真正好用的庫(kù),以此來(lái)縮短我們的開(kāi)發(fā)周期和提升軟件的質(zhì)量。

本文中,我們將通過(guò)分析 pod 安裝 (pod install) 的過(guò)程,一步一步揭示 CocoaPods 背后的技術(shù)。

核心組件

CocoaPods是用 Ruby 寫(xiě)的,并由若干個(gè) Ruby 包 (gems) 構(gòu)成的。在解析整合過(guò)程中,最重要的幾個(gè) gems 分別是: CocoaPods/CocoaPods, CocoaPods/Core, 和 CocoaPods/Xcodeproj (是的,CocoaPods 是一個(gè)依賴管理工具 -- 利用依賴管理進(jìn)行構(gòu)建的!)。

編者注 CocoaPods 是一個(gè) objc 的依賴管理工具,而其本身是利用 ruby 的依賴管理 gem 進(jìn)行構(gòu)建的

CocoaPods/CocoaPod

這是是一個(gè)面向用戶的組件,每當(dāng)執(zhí)行一個(gè) pod 命令時(shí),這個(gè)組件都將被激活。該組件包括了所有使用 CocoaPods 涉及到的功能,并且還能通過(guò)調(diào)用所有其它的 gems 來(lái)執(zhí)行任務(wù)。

CocoaPods/Core

Core 組件提供支持與 CocoaPods 相關(guān)文件的處理,文件主要是 Podfile 和 podspecs。

Podfile

Podfile 是一個(gè)文件,用于定義項(xiàng)目所需要使用的第三方庫(kù)。該文件支持高度定制,你可以根據(jù)個(gè)人喜好對(duì)其做出定制。更多相關(guān)信息,請(qǐng)查閱 Podfile 指南。

Podspec

.podspec 也是一個(gè)文件,該文件描述了一個(gè)庫(kù)是怎樣被添加到工程中的。它支持的功能有:列出源文件、framework、編譯選項(xiàng)和某個(gè)庫(kù)所需要的依賴等。

CocoaPods/Xcodeproj

這個(gè) gem 組件負(fù)責(zé)所有工程文件的整合。它能夠?qū)?chuàng)建并修改 .xcodeproj.xcworkspace 文件。它也可以作為單獨(dú)的一個(gè) gem 包使用。如果你想要寫(xiě)一個(gè)腳本來(lái)方便的修改工程文件,那么可以使用這個(gè) gem。

運(yùn)行 pod install 命令

當(dāng)運(yùn)行 pod install 命令時(shí)會(huì)引發(fā)許多操作。要想深入了解這個(gè)命令執(zhí)行的詳細(xì)內(nèi)容,可以在這個(gè)命令后面加上 --verbose?,F(xiàn)在運(yùn)行這個(gè)命令 pod install --verbose,可以看到類似如下的內(nèi)容:

$ pod install --verbose

Analyzing dependencies

Updating spec repositories
Updating spec repo `master`
  $ /usr/bin/git pull
  Already up-to-date.

Finding Podfile changes
  - AFNetworking
  - HockeySDK

Resolving dependencies of `Podfile`
Resolving dependencies for target `Pods' (iOS 6.0)
  - AFNetworking (= 1.2.1)
  - SDWebImage (= 3.2)
    - SDWebImage/Core

Comparing resolved specification to the sandbox manifest
  - AFNetworking
  - HockeySDK

Downloading dependencies

-> Using AFNetworking (1.2.1)

-> Using HockeySDK (3.0.0)
  - Running pre install hooks
    - HockeySDK

Generating Pods project
  - Creating Pods project
  - Adding source files to Pods project
  - Adding frameworks to Pods project
  - Adding libraries to Pods project
  - Adding resources to Pods project
  - Linking headers
  - Installing libraries
    - Installing target `Pods-AFNetworking` iOS 6.0
      - Adding Build files
      - Adding resource bundles to Pods project
      - Generating public xcconfig file at `Pods/Pods-AFNetworking.xcconfig`
      - Generating private xcconfig file at `Pods/Pods-AFNetworking-Private.xcconfig`
      - Generating prefix header at `Pods/Pods-AFNetworking-prefix.pch`
      - Generating dummy source file at `Pods/Pods-AFNetworking-dummy.m`
    - Installing target `Pods-HockeySDK` iOS 6.0
      - Adding Build files
      - Adding resource bundles to Pods project
      - Generating public xcconfig file at `Pods/Pods-HockeySDK.xcconfig`
      - Generating private xcconfig file at `Pods/Pods-HockeySDK-Private.xcconfig`
      - Generating prefix header at `Pods/Pods-HockeySDK-prefix.pch`
      - Generating dummy source file at `Pods/Pods-HockeySDK-dummy.m`
    - Installing target `Pods` iOS 6.0
      - Generating xcconfig file at `Pods/Pods.xcconfig`
      - Generating target environment header at `Pods/Pods-environment.h`
      - Generating copy resources script at `Pods/Pods-resources.sh`
      - Generating acknowledgements at `Pods/Pods-acknowledgements.plist`
      - Generating acknowledgements at `Pods/Pods-acknowledgements.markdown`
      - Generating dummy source file at `Pods/Pods-dummy.m`
  - Running post install hooks
  - Writing Xcode project file to `Pods/Pods.xcodeproj`
  - Writing Lockfile in `Podfile.lock`
  - Writing Manifest in `Pods/Manifest.lock`

Integrating client project

可以上到,整個(gè)過(guò)程執(zhí)行了很多操作,不過(guò)把它們分解之后,再看看,會(huì)發(fā)現(xiàn)它們都很簡(jiǎn)單。讓我們逐步來(lái)分析一下。

讀取 Podfile 文件

你是否對(duì) Podfile 的語(yǔ)法格式感到奇怪過(guò),那是因?yàn)檫@是用 Ruby 語(yǔ)言寫(xiě)的。相較而言,這要比現(xiàn)有的其他格式更加簡(jiǎn)單好用一些。

在安裝期間,第一步是要弄清楚顯示或隱式的聲明了哪些第三方庫(kù)。在加載 podspecs 過(guò)程中,CocoaPods 就建立了包括版本信息在內(nèi)的所有的第三方庫(kù)的列表。Podspecs 被存儲(chǔ)在本地路徑 ~/.cocoapods 中。

版本控制和沖突

CocoaPods 使用語(yǔ)義版本控制 - Semantic Versioning 命名約定來(lái)解決對(duì)版本的依賴。由于沖突解決系統(tǒng)建立在非重大變更的補(bǔ)丁版本之間,這使得解決依賴關(guān)系變得容易很多。例如,兩個(gè)不同的 pods 依賴于 CocoaLumberjack 的兩個(gè)版本,假設(shè)一個(gè)依賴于 2.3.1,另一個(gè)依賴于 2.3.3,此時(shí)沖突解決系統(tǒng)可以使用最新的版本 2.3.3,因?yàn)檫@個(gè)可以向后與 2.3.1 兼容。

但這并不總是有效。有許多第三方庫(kù)并不使用這樣的約定,這讓解決方案變得非常復(fù)雜。

當(dāng)然,總會(huì)有一些沖突需要手動(dòng)解決。如果一個(gè)庫(kù)依賴于 CocoaLumberjack 的 1.2.5,另外一個(gè)庫(kù)則依賴于 2.3.1,那么只有最終用戶通過(guò)明確指定使用某個(gè)版本來(lái)解決沖突。

加載源文件

CocoaPods 執(zhí)行的下一步是加載源碼。每個(gè) .podspec 文件都包含一個(gè)源代碼的索引,這些索引一般包裹一個(gè) git 地址和 git tag。它們以 commit SHAs 的方式存儲(chǔ)在 ~/Library/Caches/CocoaPods 中。這個(gè)路徑中文件的創(chuàng)建是由 Core gem 負(fù)責(zé)的。

CocoaPods 將依照 Podfile、.podspec 和緩存文件的信息將源文件下載到 Pods 目錄中。

生成 Pods.xcodeproj

每次 pod install 執(zhí)行,如果檢測(cè)到改動(dòng)時(shí),CocoaPods 會(huì)利用 Xcodeproj gem 組件對(duì) Pods.xcodeproj 進(jìn)行更新。如果該文件不存在,則用默認(rèn)配置生成。否則,會(huì)將已有的配置項(xiàng)加載至內(nèi)存中。

安裝第三方庫(kù)

當(dāng) CocoaPods 往工程中添加一個(gè)第三方庫(kù)時(shí),不僅僅是添加代碼這么簡(jiǎn)單,還會(huì)添加很多內(nèi)容。由于每個(gè)第三方庫(kù)有不同的 target,因此對(duì)于每個(gè)庫(kù),都會(huì)有幾個(gè)文件需要添加,每個(gè) target 都需要:

  • 一個(gè)包含編譯選項(xiàng)的 .xcconfig 文件
  • 一個(gè)同時(shí)包含編譯設(shè)置和 CocoaPods 默認(rèn)配置的私有 .xcconfig 文件
  • 一個(gè)編譯所必須的 prefix.pch 文件
  • 另一個(gè)編譯必須的文件 dummy.m

一旦每個(gè) pod 的 target 完成了上面的內(nèi)容,整個(gè) Pods target 就會(huì)被創(chuàng)建。這增加了相同文件的同時(shí),還增加了另外幾個(gè)文件。如果源碼中包含有資源 bundle,將這個(gè) bundle 添加至程序 target 的指令將被添加到 Pods-Resources.sh 文件中。還有一個(gè)名為 Pods-environment.h 的文件,文件中包含了一些宏,這些宏可以用來(lái)檢查某個(gè)組件是否來(lái)自 pod。最后,將生成兩個(gè)認(rèn)可文件,一個(gè)是 plist,另一個(gè)是 markdown,這兩個(gè)文件用于給最終用戶查閱相關(guān)許可信息。

寫(xiě)入至磁盤(pán)

直到現(xiàn)在,許多工作都是在內(nèi)存中進(jìn)行的。為了讓這些成果能被重復(fù)利用,我們需要將所有的結(jié)果保存到一個(gè)文件中。所以 Pods.xcodeproj 文件被寫(xiě)入磁盤(pán),另外兩個(gè)非常重要的文件:Podfile.lockManifest.lock 都將被寫(xiě)入磁盤(pán)。

Podfile.lock

這是 CocoaPods 創(chuàng)建的最重要的文件之一。它記錄了需要被安裝的 pod 的每個(gè)已安裝的版本。如果你想知道已安裝的 pod 是哪個(gè)版本,可以查看這個(gè)文件。推薦將 Podfile.lock 文件加入到版本控制中,這有助于整個(gè)團(tuán)隊(duì)的一致性。

Manifest.lock

這是每次運(yùn)行 pod install 命令時(shí)創(chuàng)建的 Podfile.lock 文件的副本。如果你遇見(jiàn)過(guò)這樣的錯(cuò)誤 沙盒文件與 Podfile.lock 文件不同步 (The sandbox is not in sync with the Podfile.lock),這是因?yàn)?Manifest.lock 文件和 Podfile.lock 文件不一致所引起。由于 Pods 所在的目錄并不總在版本控制之下,這樣可以保證開(kāi)發(fā)者運(yùn)行 app 之前都能更新他們的 pods,否則 app 可能會(huì) crash,或者在一些不太明顯的地方編譯失敗。

xcproj

如果你已經(jīng)依照我們的建議在系統(tǒng)上安裝了 xcproj,它會(huì)對(duì) Pods.xcodeproj 文件執(zhí)行一下 touch 以將其轉(zhuǎn)換成為舊的 ASCII plist 格式的文件。為什么要這么做呢?雖然在很久以前就不被其它軟件支持了,但是 Xcode 仍然依賴于這種格式。如果沒(méi)有 xcproj,你的 Pods.xcodeproj 文件將會(huì)以 XML 格式的 plist 文件存儲(chǔ),當(dāng)你用 Xcode 打開(kāi)它時(shí),它會(huì)被改寫(xiě),并造成大量的文件改動(dòng)。

結(jié)果

運(yùn)行 pod install 命令的最終結(jié)果是許多文件被添加到你的工程和系統(tǒng)中。這個(gè)過(guò)程通常只需要幾秒鐘。當(dāng)然沒(méi)有 Cocoapods 這些事也都可以完成。只不過(guò)所花的時(shí)間就不僅僅是幾秒而已了。

補(bǔ)充:持續(xù)集成

CocoaPods 和持續(xù)集成在一起非常融洽。雖然持續(xù)集成很大程度上取決于你的項(xiàng)目配置,但 Cocoapods 依然能很容易地對(duì)項(xiàng)目進(jìn)行編譯。

Pods 文件夾的版本控制

如果 Pods 文件夾和里面的所有內(nèi)容都在版本控制之中,那么你不需要做什么特別的工作,就能夠持續(xù)集成。我們只需要給 .xcworkspace 選擇一個(gè)正確的 scheme 即可。

不受版本控制的 Pods 文件夾

如果你的 Pods 文件夾不受版本控制,那么你需要做一些額外的步驟來(lái)保證持續(xù)集成的順利進(jìn)行。最起碼,Podfile 文件要放入版本控制之中。另外強(qiáng)烈建議將生成的 .xcworkspacePodfile.lock 文件納入版本控制,這樣不僅簡(jiǎn)單方便,也能保證所使用 Pod 的版本是正確的。

一旦配置完畢,在持續(xù)集成中運(yùn)行 CocoaPods 的關(guān)鍵就是確保每次編譯之前都執(zhí)行了 pod install 命令。在大多數(shù)系統(tǒng)中,例如 Jenkins 或 Travis,只需要定義一個(gè)編譯步驟即可 (實(shí)際上,Travis 會(huì)自動(dòng)執(zhí)行 pod install 命令)。對(duì)于 Xcode Bots,在書(shū)寫(xiě)這篇文章時(shí)我們還沒(méi)能找到非常流暢的方式,不過(guò)我們正朝著解決方案努力,一旦成功,我們將會(huì)立即分享。

結(jié)束語(yǔ)

CocoaPods 簡(jiǎn)化了 Objective-C 的開(kāi)發(fā)流程,我們的目標(biāo)是讓第三方庫(kù)更容易被發(fā)現(xiàn)和添加。了解 CocoaPods 的原理能讓你做出更好的應(yīng)用程序。我們沿著 CocoaPods 的整個(gè)執(zhí)行過(guò)程,從載入 specs 文件和源代碼、創(chuàng)建 .xcodeproj 文件和所有組件,到將所有文件寫(xiě)入磁盤(pán)。所以接下來(lái),我們運(yùn)行 pod install --verbose,靜靜觀察 CocoaPods 的魔力如何顯現(xiàn)。