隨着業務的擴展、項目體積的增大,CocoaPods
組件庫愈來愈多,每次從新編譯的時候速度愈來愈慢,這給咱們提出了須要提升編譯速度的需求。html
爲了提升項目編譯速度,對於大量使用組件化開發的項目組而言,組件二進制化是必然要走的路線,雖然中心思想就是要將各個組件打包成.a
二進制庫,可是各個公司可能方案都不太相同,網上的方案也有不少可供選擇,這裏我大致總結成如下幾種:ios
Carthage
管理podspec
環境變量(宏管理)podspec
分tag
管理(只針對私有庫)前兩個就不在這裏討論了能夠看看這篇講解。今天重點給你們分享一下第三和第四種方案的實施,可是目前只能針對私有庫實施,對於一些第三方的公有庫目前沒有什麼好的方案(😁 有好方法的同窗能夠在評論區推薦一下)。git
😝 若是您對這一塊很瞭解請跳過這一步直接看第二步github
對於私有庫的建立,通常咱們會採用pod lib create XXX
模板來進行構建(若是還不知道這條命令是幹嗎的同窗能夠先移步瞭解一下理解CocoaPods的Pod Lib Create)xcode
這裏咱們拿ABC
這個項目進行舉例,首先咱們執行pod lib create ABC
建立ABC
的私有庫 CocoaPods
會從https://github.com/CocoaPods/pod-template.git
下載模板文件,並詢問你一些構建信息,正常填就行了。緩存
[MichaeldeMacBook-Pro:~ michaelwu$ pod lib create ABC
Cloning `https://github.com/CocoaPods/pod-template.git` into `ABC`.
Configuring ABC template.
------------------------------
To get you started we need to ask a few questions, this should only take a minute.
If this is your first time we recommend running through with the guide:
- https://guides.cocoapods.org/making/using-pod-lib-create.html
( hold cmd and double click links to open in a browser. )
What platform do you want to use?? [ iOS / macOS ]
>
複製代碼
通常若是咱們構建好了的話工程目錄會相似這樣一個結構:ruby
.
├── ABC
│ ├── Assets
│ └── Classes
├── ABC.podspec
├── Example
│ ├── ABC
│ ├── ABC.xcodeproj
│ ├── ABC.xcworkspace
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
複製代碼
這裏你會發現,CocoaPods
已經幫咱們建立好了Demo
、源文件目錄、Podfile
、podspec
、.gitignore
文件等(真是一個貼心的小傢伙),並且很規範,Demo
文件在Example
目錄下bash
窺視一下podspec
文件你就明白了源碼須要指定在./Classes/**/*
路徑下運維
s.source_files = 'ABC/Classes/**/*'
複製代碼
爲了演示效果,咱們建立兩個源文件ABC.h
與ABC.m
並放入Classes
路徑下,同時將默認的ReplaceMe.m
刪除 ssh
接着在Example
下執行pod install
,能夠發現ABC.h/m
已經導入成功
至此,咱們就明白了私有庫的建立過程,須要編寫源代碼須要放入指定目錄下並在執行pod install
進行同步
組件二進制其實指的就是打包成動態庫/靜態庫,因爲過多的動態庫會致使啓動速度減慢得不償失,此外iOS
對於動態庫的表現形式只有framework
,若想作源碼與二進制切換時,引入頭文件的地方也不得不進行更改,例如:
import <ABC.h> // 源碼引用
import <ABCBinary/ABC.h> // 動態庫引用
複製代碼
而打包成靜態庫.a
文件(注意不要打包成framework
形式)則不須要更改引用代碼,因此綜上所述,咱們選擇打包成靜態庫的方式不需修改引用代碼、縮小體積提高編譯速度。
肯定目標以後,就是實施了,通常而言咱們私有庫都會在遠程託管地址有git
倉庫,而後再上傳到指定的私有源(specs)上,那麼就會引伸出幾個問題:
git
(若是包體積很大會很佔用git
空間)針對這幾個問題,一一回答:
其實這個很簡單,咱們接着拿ABC
這個項目舉例子,進入Example
打開咱們的ABC.xcworkspace
工程,而後建立新的Target
爲靜態庫,並取名爲ABCBinary
(必定要取這個名字,後面我會解釋)
File->New->Target->Static Library
複製代碼
此時在Example
目錄下會增長剛剛建立的Target
文件夾,結構以下:
├── ABCBinary
│ ├── ABCBinary.h
│ └── ABCBinary.m
複製代碼
Xcode默認會幫咱們生成兩個文件,咱們將.h
更名爲placeholder.h
,.m
刪除,這裏爲何要將.h
換成placeholder.h
呢?先賣個關子,待會咱們再做解釋。
咱們把剛纔寫的ABC.h/m
的源碼拖到ABCBinary
中,注意不要勾選Copy items if needed
,只作引用便可
以後咱們須要到ABCBinary
的Build Setting
中指定靜態庫所能運行的最低版本:
Build Setting->Deployment->iOS Deployment Target
複製代碼
並在Build Phases
中指定頭文件,將ABC.h
拖入Public中,具體步驟:
TARGETS->ABCBinary->Build Phases->New Header Phase
複製代碼
至此咱們完成了一套代碼管理二進制與源碼,但有個小細節須要注意:就是若是源代碼有變更須要在XXXBinary
文件中從新導入一遍,否則二進制的文件不會自動更新(同窗們有好的建議能夠評論區討論下)
其實git
對代碼管理時會將不一樣的diff
作備份(在.git
這個文件夾下),可是對於二進制文件來講git
就沒用那麼友好了,會將二進制的每一次提交都作磁盤備份,以便於隨時版本回滾,假若咱們每次都對私有庫進行更新時都將二進制包傳至git
,那麼時間久了無疑是對git
倉庫空間的一個挑戰(若是大家公司空間足夠大不須要考慮,那麼請忽略這一步)
網上有不少針對這個問題給出的解決方案,但都不是很完美,大致上都是說將二進制包
單獨傳到另外一份靜態資源地址,以此解決git
過大問題,不過我以爲沒有解決痛點,能不能不上傳二進制包呢?
結論固然是能夠,CocoaPods
本地的緩存目錄在
~/Library/Caches/Cocoapods
複製代碼
其實每次咱們更新pod
庫時,CocoaPods
都會先從指定源去拉源代碼再根據該庫的podspec
文件指定輸出目標文件,那麼咱們若是能把靜態庫打包推遲到pod install
階段就不須要上傳二進制包到git
了,可是如何作到延遲打包呢?
很幸運,CocoaPods
提供了針對podspec
的預執行腳本,prepare_command(戳我進官網)命令,該命令能夠指定相應的腳本在pod install
時去執行,那麼咱們就能夠將編譯打包的腳本放入其中,從而完成延遲打包
好了,理論上貌似可行了,實踐出真知啊(😄 絕對不能作一個理論性選手啊),具體怎麼作?
首先咱們須要一個能一鍵打靜態庫包的腳本(一刀99級那種),帥氣的我這邊已經爲你們準備好了,只修改一下PROJECT_NAME
便可,拷貝腳本至根目錄並賦予執行權限:
# 當前項目名字,須要修改!
PROJECT_NAME='ABC'
# 編譯工程
BINARY_NAME="${PROJECT_NAME}Binary"
cd Example
INSTALL_DIR=$PWD/../Pod/Products
rm -fr "${INSTALL_DIR}"
mkdir $INSTALL_DIR
WRK_DIR=build
BUILD_PATH=${WRK_DIR}
DEVICE_INCLUDE_DIR=${BUILD_PATH}/Release-iphoneos/usr/local/include
DEVICE_DIR=${BUILD_PATH}/Release-iphoneos/lib${BINARY_NAME}.a
SIMULATOR_DIR=${BUILD_PATH}/Release-iphonesimulator/lib${BINARY_NAME}.a
RE_OS="Release-iphoneos"
RE_SIMULATOR="Release-iphonesimulator"
xcodebuild -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphoneos clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_OS}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_OS}"
xcodebuild ARCHS=x86_64 ONLY_ACTIVE_ARCH=NO -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphonesimulator clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_SIMULATOR}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_SIMULATOR}"
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -rp "${DEVICE_INCLUDE_DIR}" "${INSTALL_DIR}/"
INSTALL_LIB_DIR=${INSTALL_DIR}/lib
mkdir -p "${INSTALL_LIB_DIR}"
lipo -create "${DEVICE_DIR}" "${SIMULATOR_DIR}" -output "${INSTALL_LIB_DIR}/lib${PROJECT_NAME}.a"
rm -r "${WRK_DIR}"
複製代碼
咱們仍是拿ABC
的項目來接着實踐,拷貝腳本後,先來看一下咱們ABC
目前的結構:
.
├── ABC
│ ├── Assets
│ └── Classes
├── ABC.podspec
├── Example
│ ├── ABC
│ ├── ABC.xcodeproj
│ ├── ABC.xcworkspace
│ ├── ABCBinary
│ │ └── placeholder.h
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── LICENSE
├── README.md
├── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
└── build_lib.sh
複製代碼
能夠看到最下面多了一個build_lib.sh
腳本(就是剛剛拷貝的那個腳本),另外ABCBinary
裏面有一個placeholder.h
,這裏解釋一下以前埋下的懸念:由於ABCBinary
文件夾裏對於源碼的引用沒有copy
,因此在提交到git
時會自動將文件夾清空(也就是說在git目錄裏找不到),所以須要加一個佔位防止文件夾不上傳到git
,可是切記不要編譯到靜態庫裏!
好的,至此一鍵打包腳本也準備好了,經過查看腳本咱們發現這個二進制包最終會輸出到根目錄下的./Pod/Products/
目錄中,那不仍是得傳到git
嗎?別急,你忘了gitignore
了嗎?
配置.gitignore
忽略Pod/
文件不就好了嘛,在.gitignore
最下面增長忽略
Pod/
複製代碼
好了至此,咱們完成了自動打包腳本及git
忽略二進制包,不再用擔憂咱們的git
倉庫空間壓力了(運維小哥哥們表示「尼瑪鬆了一口氣」)
在提高編譯速度的前提下,還須要考慮到能隨時進行源碼調試,這就涉及到了如何在源碼與二進制間切換的問題,網上的思路有不少:環境變量、白名單、tag切換等。
這幾種方式在前言部分咱們已經講過了,接下來咱們介紹一下「環境變量」和「tag切換」這兩種方式:
首先咱們須要約定好規則:當version
中包含.Binary
關鍵字時執行prepare_command
命令並輸出source
爲靜態庫,具體操做以下(podspec
是用ruby
寫的,支持條件判斷):
if s.version.to_s.include?'Binary'
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is binary now'
puts '-------------------------------------------------------------------'
s.prepare_command = '/bin/bash build_lib.sh'
s.source_files = 'Pod/Products/include/**'
s.ios.vendored_libraries = 'Pod/Products/lib/*.a'
s.public_header_files = 'Pod/Products/include/*.h'
else
s.source_files = 'ABC/Classes/**/*'
end
複製代碼
因爲tag
是根據version
走的(tag => s.version.to_s
),所以只須要咱們修改s.version = '0.1.0.Binary'
便可實現二進制打包
好,咱們貼一段此時ABC.podspec
完整的代碼:
Pod::Spec.new do |s|
s.name = 'ABC'
s.version = '0.1.0.Binary'
s.summary = 'A short description of ABC.'
s.description = <<-DESC TODO: Add long description of the pod here. DESC
s.homepage = 'https://github.com/609223770@qq.com/ABC'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { '609223770@qq.com' => '609223770@qq.com' }
s.source = { :git => 'https://github.com/609223770@qq.com/ABC.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '8.0'
if s.version.to_s.include?'Binary'
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is binary now'
puts '-------------------------------------------------------------------'
s.prepare_command = '/bin/bash build_lib.sh'
s.source_files = 'Pod/Products/include/**'
s.ios.vendored_libraries = 'Pod/Products/lib/*.a'
s.public_header_files = 'Pod/Products/include/*.h'
else
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is source code now'
puts '-------------------------------------------------------------------'
s.source_files = 'ABC/Classes/**/*'
end
end
複製代碼
讓咱們來看看效果,在Example
下執行pod install
,發現切換過來了,Nice 😝~
接下來驗證本地podspec
(如有問題按照提示更改,ssh://xxx.git
是你私有源的地址):
pod lib lint --sources=ssh://xxx.git --allow-warnings --verbose --use-libraries
複製代碼
若沒問題,在ABC
的git
倉庫打一個0.1.0
的版本tag
,並上傳ABC.podspec
至私有源,上傳成功後修改podspec.version
爲0.1.0.Binary
再次執行上傳:
pod repo push XXXSpecs ABC.podspec --allow-warnings --verbose --use-libraries
複製代碼
✅ 若是一切順利,咱們已經將Binary和源碼的ABC
上傳到了私有源。
接下來咱們在實際項目實驗一下,Podfile
中指定,並執行安裝
pod 'ABC', '~> 0.1.0' # source code
pod install
複製代碼
不出意外源碼ABC
安裝成功,這時咱們修改tag
版本後面加.Binary
,再次執行pod install
,以下所示:
pod 'ABC', '~> 0.1.0.Binary' # source code
pod install
複製代碼
很遺憾,你可能會發現源碼並無切換成功,爲何呢?
原來Pod
的版本管理是放在Podfile.lock
中,每次執行pod install
時若Podfile.lock
中已經存在此庫,則只下載Podfile.lock
文件中指定的版本進行安裝,不然去搜索這個pod
庫在Podfile
文件中指定的版原本安裝。
所以,解決辦法有兩種,一種是從Podfile.lock
中將包含ABC
的地方所有刪除或是乾脆直接刪除Podfile.lock
,再次執行pod install
會發現切換變過來了。
還有一種方法是執行pod update
,這也是 update 和 install 的區別,update會讀取Podfile
中的版本去更新Podfile.lock
文件。(戳我查看pod install和pod update區別)
pod update ABC
複製代碼
執行後,先是會更新一下master和其餘私有源,再去更新ABC
,發現此時切換成功。(缺點就是若是Podfile
中若是某些庫沒有指定版本就會更新到最新版本)
Ruby語法支持一些環境變量的讀取,所以能夠在pod install
時增長參數以此判斷是否要切換源碼:
IS_BINARY=1 pod install # 1 表明二進制
IS_BINARY=0 pod install # 0 表明源碼
pod install # 默認也是0 源碼
複製代碼
在podspec
中作修改:
Pod::Spec.new do |s|
s.name = 'ABC'
s.version = '0.1.0.Binary'
s.summary = 'A short description of ABC.'
s.description = <<-DESC TODO: Add long description of the pod here. DESC
s.homepage = 'https://github.com/609223770@qq.com/ABC'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { '609223770@qq.com' => '609223770@qq.com' }
s.source = { :git => 'https://github.com/609223770@qq.com/ABC.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '8.0'
if s.version.to_s.include?'Binary' or ENV['IS_BINARY']
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is binary now'
puts '-------------------------------------------------------------------'
s.prepare_command = '/bin/bash build_lib.sh'
s.source_files = 'Pod/Products/include/**'
s.ios.vendored_libraries = 'Pod/Products/lib/*.a'
s.public_header_files = 'Pod/Products/include/*.h'
else
puts '-------------------------------------------------------------------'
puts 'Notice:ABC is source code now'
puts '-------------------------------------------------------------------'
s.source_files = 'ABC/Classes/**/*'
end
end
複製代碼
同tag切換同樣,這種方式在實際項目中切換也存在問題,須要兩個必要步驟:
pod cache clean ABC # 先清理ABC的pod緩存
rm Pods/ABC # 再把ABC從實際項目中的Pods目錄下移除
複製代碼
方式 | 優勢 | 缺點 |
---|---|---|
Ruby環境變量切換 | 一、不須要上傳兩份podspec 二、切換時不須要修改Podfile |
一、須要清除私有庫的緩存 二、須要手動刪除/Pods/XXX 三、不能針對單獨庫進行切換,除非自定義白名單之類的規則 |
tag切換 | 一、能夠針對單獨某個庫進行切換 | 一、須要執行pod update(需等待repo master源的更新) 二、私有庫的tag須要打兩個,podspec上傳時須要傳兩次 三、切換時須要手動修改Podfile文件的版本信息 |
好,至此iOS組件二進制方案就介紹完了,咱們經過ABC
項目的實踐瞭解了整個過程:
.gitignore
忽略輸出的二進制包pod update
或刪除Podfile.lock
中相關庫信息本文demo相關連接以下,另附自動上傳podspec腳本地址(相關文章),喜歡的朋友點個star