智人能在殘酷的進化大戰中存活下來,緣由之一就是智人懂得將知識沉澱成外物,輔助彼此之間的合做,從而使得整個羣體產生了規模效應,即1+1>2的效果。 從一個角度上說,石器時代是基於石器的組件化的時代,由於老張家的石矛(或其它石頭利器)借給了老王,同樣能夠拿去狩獵。要想實現這個目的,必定要保證:html
一種觀點認爲,信息時代是基於軟件構建起來的,由工程師不斷貢獻智力和體力,從而產生價值的時代。產品需求就好像前文說到的獵物,完成需求相似於成功捕殺獵物,而產品逾期就比如被獵物吃掉。所以在這個時代,也須要一些能夠好用且容易使用的功能代碼段,方便程序員拿來快速實現需求,就比如遠古時代的能夠複用的石矛。製做這種功能代碼段的過程叫作組件化,這種方法帶來的產出叫作組件,俗稱輪子。ios
從本質上說,組件是經過庫的方式來進行封裝從而提供給開發者使用。而庫,就是一種組織一個或多個文件的方式。在 iOS 8 以前,iOS 只支持以靜態庫的方式來使用第三方的代碼。c++
靜態庫,在iOS中會被打包成.a文件,配合.h頭文件一塊兒能夠完成功能的調用。可是在在概念上,靜態庫是一種All In One的設計思路,由於依賴靜態庫的代碼會把靜態庫徹底連接到App的可執行文件中。也就是說,靜態庫是在編譯器被連接到App中的,所以若是多個App都引用了同一個靜態庫,則每一個App都會把這個靜態庫連接一份,這其實浪費了內存。 固然,靜態庫的缺點不止於此。在使用靜態庫時,必須手動一個個連接它依賴的外部庫,例如早期微信支付SDK的靜態庫接入方法中,必需要手動連接上:git
SystemConfiguration.framework,
libz.dylib,
libsqlite3.0.dylib,
libc++.dylib,
Security.framework,
CoreTelephony.framework,
CFNetwork.framework
複製代碼
有沒有一種須要輪流背誦蒸羊羔、蒸熊掌、蒸鹿尾兒、燒花鴨、燒雛雞、燒子鵝、滷豬。。。
的既視感。 並且,靜態庫的特色致使了App每次啓動時都要從新加載靜態庫的內存,沒法控制加載時機,並且每次啓動都須要從新加載靜態庫,致使二次加載時間沒法被優化。 大部分時候,還須要在Other Linker Flags裏填入Objc -all_load
來確保靜態庫正常工做。 好吧,聽起來靜態庫很難用。 咱們都知道,後期iOS支持了動態庫。那動態庫是否是就能完美解決問題了呢?程序員
動態庫,大部分會被打包成.tbd文件或者.dylib文件。不一樣於靜態庫在編譯期連接到App,動態庫是在運行時連接到App的,所以它有了三個好處:github
不過,蘋果對動態庫的徹底支持僅停留在系統的動態庫上,例如UI.framework,對於第三方的動態庫,仍是須要embed到系統中。早期的一些熱更新框架,例如JSPatch鑽了漏子經過dlopen來進行熱更新,不過很快被禁掉了。 不過,若是是企業證書,仍是能夠在本身的app裏靈活的加載第三方動態庫的。算法
在解釋靜態庫和動態庫的過程當中,我並無提framework的字眼。有些開發者以爲framework文件就是動態庫,其實並不許確。 咱們提到的framework,指的是.framework文件,這既不必定是靜態庫,也不必定是動態庫。實際上這是一種打包方式,將Header(頭文件)、Binary(二進制代碼文件)和bundle(資源文件)一塊兒打包,方便開發者進行接入和調用。 所以framework究竟是靜態庫仍是動態庫,取決於Binary文件(Mach-O文件)究竟是靜態庫仍是動態庫。sql
「老一輩」的iOS開發都會記得手動引入靜態庫時,那無止境的編譯錯誤。我簡單總結一下,若是手動引入靜態庫,須要:xcode
程序員的創造力不少時候來源於「懶」,終於,CocoaPods橫空出世,今後開啓了一行命令行完成模塊集成的時代!bash
CocoaPods是iOS平臺當前最流行的包管理工具,能夠將它理解爲一個能夠自動部署到項目的組件池,而對應的podfile文件就至關於請求組件的Request。當組件下載到工程後,cocoaPods會自動完成組件集成到現有項目的工做,並完成修改.xcodeproj文件和建立.xcworkspace文件。最終將全部組件統一打包成Pods.framework靜態庫,供項目使用。
在CocoaPods中,會存在如下幾種文件:
pod install
,那具體在執行pod install
時發生了什麼呢?當咱們運行pod install
時,會發生:
The sandbox is not sync with the podfile.lock
這種錯誤,則表示manifest.lock和podfile.lock文件不一致),此時通常須要從新運行pod install
命令。Embed Pods Frameworks
Copy Pod Resources
其中,pre-install hook和post-install hook能夠理解成回調函數,是在podfile裏對於install以前或者以後(生成工程可是還沒寫入磁盤)能夠執行的邏輯,邏輯爲:
pre_install do |installer|
# 作一些安裝以前的hook
end
post_install do |installer|
# 作一些安裝以後的hook
end
複製代碼
git clone xxxx.git
注意,此時必需要保證須要下載的pod版本號和git倉庫的tag標籤號一致。全部依賴庫下載以後,便進入了和Xcode工程的融合步驟。
在cocoaPods和Xcode工程進行集成的過程當中,會有有如下流程
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Demo/Demo.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
複製代碼
create group 在工程中建立group文件夾,邏輯上隔離一些文件
create pod project & add pod library 建立pod.xcodeproject工程,而且將在podfile中定義的第三方庫引入到這個工程之中。
add embed frameworks script phase 添加了[CP] Embed Pods Frameworks,相應的,多了pods_xxx的group,下列xxx.framework.sh,來完成將內部第三方庫打包成.a靜態庫文件(在Podfile中若是選擇了!use_frameworks,則此步驟會打包成.framework)
remove embed frameworks script phase 若是本次podfile刪除了部分第三方庫,則此步驟會刪除掉不須要的第三方庫,將其的引用關係從Pod.xcodeproject工程中拿走。
add copy resource script phase 若是第三方庫存在資源bundle,則此步驟會將資源文件進行復制到集中的目錄中,方便統一進行打包和封裝。相應的,會添加[CP] Copy Pods Resources腳本。
add check manifest.lock script phase 前文提到過,manifest.lock實際上是podfile.lock的副本。此步驟會進行diff,若是存在不一致,則會提示著名的那句The sandbox is not sync with the podfile.lock
錯誤。
add user script phase 此步驟是對原有project工程文件進行改造。在運行過pod install
後,再次打開原有工程會發現沒法編譯經過,由於已經作了改動。
首先,添加了對Pod工程的依賴,具體爲引用中多了libPods_xxx.a文件。此步驟的.a文件(或者.framework文件)爲上述步驟中xxx.framework.sh打包出來的文件,也就是說,cocoaPods會把全部第三方的組件封裝爲一個.a文件(或者.framework文件)!
創建了Pods的group,內含pods-xxx-debug.xconfig和pods-xxx.release.xconfig文件。這兩個文件是對應工程的build phase的配置。相應的,主工程的Iinfo->Configurations的debug和release配置會對應上述兩個配置文件。
上述兩個配置都作了什麼?包括: Header_search_path,指向了Pod/Headers/public/xxx,添加了Pods文件編譯後的頭文件地址 Other_LDFLAGS,添加了-ObjC等等 一些Pods變了,例如Pods_BUILD_DIR等
至此,原有xcode工程和新建的Pod工程完成了集成和融合。
好了,cocoaPods的好處和原理已經介紹的差很少了。大部分時間,咱們經過引用github上的組件就夠用了。可是有時候處於業務須要,咱們須要來實現私有Pod庫。因此接下來咱們來介紹下如何在公司內網來實現一個私有庫,實現一個私有組件。
所謂Spec Repo,就是Pods的索引。一旦在podfile中設置source爲某個私有repo的git地址,在進行pod update的時候就會去這個repo中進行檢索,若是檢索到對應的pod,會讀取該Pod的podspec從而進行安裝。 一個Spec Repo的目錄結構以下:
以後咱們去git.xxx.com上新建一個相應的Repo地址,以後添加repo到本地,該repo地址是爲了後面提交podspec使用。
# pod repo add [Private Repo Name] [GitHub HTTPS clone URL]
pod repo add XXXCocoaPodsRepo git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git
複製代碼
成功後能夠進入~/.cocoapods/repos
目錄下查看XXXCocoaPodsRepo
這個目錄了。
這裏,咱們以HelloXXXPod爲例。 去git.xxx.com上去新建項目,以後獲取地址,爲:
git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git
複製代碼
此時clone到本地,命令爲:
git clone git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git
複製代碼
這裏建議經過CocoPods的官方命令來進行Pod項目的建立,以測試項目HelloXXXPod爲例,命令以下:
pod lib create HelloXXXPod
複製代碼
不出意外地話,會提問你六個問題(cocoaPods v1.5.3版本):
1.What platform do you want to use? [ iOS / macOS ]
2.What language do you want to use? [ Swift / ObjC ]
3.Would you like to include a demo application with your library? [ Yes / No ]
4.Which testing frameworks will you use? [ Specta / Kiwi / None ]
5.Would you like to do view based testing? [ Yes / No ]
6.What is your class prefix?
複製代碼
分別解釋一下
What platform do you want to use?? [ iOS / macOS ] 問組件化應用在哪一個平臺上,通常咱們選iOS
What language do you want to use? [ Swift / ObjC ] 使用何種語言,能夠根據項目是OC仍是Swift自行選擇
Would you like to include a demo application with your library? [ Yes / No ] 問是否須要一個Demo工程,方便調試Pod。若是是第一次作組件化,建議選Yes,方便pod的調試
Which testing frameworks will you use? [ Specta / Kiwi / None ] 問是否須要UT測試框架,可選擇Specta和Kiwi,或者選擇不要。
Specta是OC的一個輕量級TDD/BDD框架,參考github/specta
Kiwi是一個iOS的一個BDD框架,能夠簡單地部署和使用。github/kiwi UT測試框架若是要選擇的話,建議選擇Kiwi,能夠參考我以前寫的調研kiwi上手體驗 本次的Demo,暫時選None
Would you like to do view based testing? [ Yes / No ] 若是上一步選擇了Specta ,這步會生成一部分有利於作自動化測試的邏輯和代碼
What is your class prefix? 這裏能夠指定你的項目前綴,這樣在new一個類時會自動加上前綴
以後咱們運行pod install
,生成的文件目錄樹結構以下:
$ tree HelloXXXPod -L 2
HelloXXXPod
├── Example
│ ├── Build
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ ├── Tests
│ ├── helloXXXPod
│ ├── helloXXXPod.xcodeproj
│ └── helloXXXPod.xcworkspace
├── LICENSE
├── README.md
├── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
├── helloXXXPod
│ ├── Assets
│ └── Classes
└── helloXXXPod.podspec
複製代碼
這時候能夠在剛纔生成的Example工程內作開發,這時候記得把新建的代碼放到Classes目錄下。若是有圖片資源,建議放到Assets下。
開發、調試完成以後,就能夠去編輯podspec文件了。按如下方式來修改,不明白的字段請參考官方文檔:
這裏給出本次Demo的podspec供各位參考:
Pod::Spec.new do |s|
s.name = 'helloXXXPod'
s.version = '0.1.0'
s.summary = 'A short description of helloXXXPod.'
s.description = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://git.xxx.com/XXX_SPA_XXX/HelloXXXPod'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'nimomeng' => 'nimomeng@tencent.com' }
s.source = { :git => 'git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = 'helloXXXPod/Classes/**/*'
end
複製代碼
其中,注意修改這幾個字段:
若是是經過pod lib create
命令建立的Pod,會在Example中自動配置好該pod的本地調試腳本,以下:
use_frameworks!
platform :ios, '8.0'
target 'helloXXXPod_Example' do
pod 'helloXXXPod' :path => '../'
target 'helloXXXPod_Tests' do
inherit! :search_paths
end
end
複製代碼
其中,pod 'helloXXXPod' :path => '../'
的含義是說,在上層目錄來下載helloXXXPod
這個pod。這是本地調試Pod的一種。 一樣的,能夠實現相似方式調試的方法,還有經過:podspec
命令來指定,指定pod所在的podspec文件位置便可
其中,path語法精確到目錄便可;podspec語法必需要精確到文件。
設置好podfile以後,在Example文件下執行pod install
,則能夠發現新的文件已經出如今項目工程的pods文件夾之下了。
注意,經過path
語法進行更新後,Pod中代碼並不在Pod文件夾中,而是在一個叫 Development Pods
中。
開發完成,須要本地驗證podspec,確保其有效:
pod lib lint helloXXXPod.podspec
複製代碼
以後要作的就是把庫同步到Git上去了。這時候須要去git.xxx.com上創建一個對應的倉庫,例如:
http://git.xxx.com/XXX_SPA_XXX/HelloXXXPod.git (替換爲本身的實際git地址)
複製代碼
而後將代碼同步到此Git上。
git add .
git commit -m "Init"
git remote add origin http://git.xxx.com/XXX_SPA_XXX/HelloXXXPod.git(替換爲本身的實際git地址)
git push --set-upstream origin master
複製代碼
podSpec文件須要版本控制信息,因此咱們要打一個Tag.
git tag -m "first demo" 0.1.0
git push --tags
複製代碼
在執行本歩以前,確保最新代碼已經提交到了Git上,且已經打好了tag.
向Spec Repo提交podspec的命令:
pod repo push XXXCocoaPodsRepo HelloXXXPod.podspec --allow-warnings
複製代碼
在通過三輪的用戶校驗以後,提交成功!這時候咱們去~/.cocoapods/repos/XXXCocoaPodsRepo
中查看,咱們的的podspec已經在裏面了!
此時經過pod search HelloXXXPod
已經能夠查到了!
最後,爲了保證本地的repo已經被更新,運行pod update
來更新repo
咱們能夠在想要使用的項目中的Podfile里加入以下代碼:
pod 'helloXXXPod'
複製代碼
便可。 固然,因爲咱們的是私有CocoaPods庫,所以最好告訴系統這個庫的source在哪裏,所以在Podfile文件上部也請加上Spec Repo的git地址。同時,爲了確保公共的cocoaPod能夠被正常下載,請添加外部CocoaPod的庫:
# For inner pods
source 'git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git'
# For public pods
source 'https://github.com/CocoaPods/Specs.git'
複製代碼
整個的Podfile文件看起來是這樣的:
use_frameworks!
platform :ios, '8.0'
# source 'git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git'
# For public pods
source 'https://github.com/CocoaPods/Specs.git'
target 'helloXXXPod_Example' do
pod 'helloXXXPod'
target 'helloXXXPod_Tests' do
inherit! :search_paths
end
end
複製代碼
以後運行pod install
便可安裝對應的Pods
咱們能夠複用Example項目,只不過此次再也不經過:path
命令或者:podspec
命令來作本地調用,而是徹底使用安裝外部pod的方式,即:
pod 'helloXXXPod'
複製代碼
注意:雖然pod已經推送到線上,可是本地必定要先更新pod的repo,否則仍是沒法找到最新的pod。確保先作pod update
操做。
Example項目中,咱們調用在Pod中寫好的方法,查看是否輸入對應的log便可驗證:
至此,Pod建立完成。
若是pod中用到framework,應該在哪裏添加?
若是pod中用到framework,如AVFoundation,直接在podspec文件中添加s.frameworks = ‘AVFoundation’或者s.frameworks = [‘AVFoundation’,'MapKit'],而不該該添加在項目的Link Binary With Libraries下面。
怎麼取更新私有 pod?
更新私有pod的過程和建立pod的步驟一致,可是要記得在更改代碼後要記得必定從新run一下aggregate,更改podspec裏的s.version(由於tag不能重複提交), 從新pod repo push
若是出現這個錯誤怎麼辦:
[!] An unexpected version directory `Assets` was encountered for the `/Users/nimo/.cocoapods/repos/xxxx` Pod in the `xxxx` repository.
複製代碼
這個錯誤,請查看: