稍有 iOS 開發(fā)經(jīng)驗的人應(yīng)該都是用過 CocoaPods灵嫌,而對于 CI、CD 有了解的同學(xué)也都知道 Fastlane氛驮。而這兩個在 iOS 開發(fā)中非常便捷的第三方庫都是使用 Ruby 來編寫的腕柜,這是為什么?
先拋開這個話題不談矫废,我們來看一下 CocoaPods 和 Fastlane 是如何使用的盏缤,首先是 CocoaPods,在每一個工程使用 CocoaPods 的工程中都有一個 Podfile:
source 'https://github.com/CocoaPods/Specs.git'
target 'Demo' do
pod 'Mantle', '~> 1.5.1'
pod 'SDWebImage', '~> 3.7.1'
pod 'BlocksKit', '~> 2.2.5'
pod 'SSKeychain', '~> 1.2.3'
pod 'UMengAnalytics', '~> 3.1.8'
pod 'UMengFeedback', '~> 1.4.2'
pod 'Masonry', '~> 0.5.3'
pod 'AFNetworking', '~> 2.4.1'
pod 'Aspects', '~> 1.4.1'
end
這是一個使用 Podfile 定義依賴的一個例子蓖扑,不過 Podfile 對約束的描述其實是這樣的:
source('https://github.com/CocoaPods/Specs.git')
target('Demo') do
pod('Mantle', '~> 1.5.1')
...
end
Ruby 代碼在調(diào)用方法時可以省略括號唉铜。
Podfile 中對于約束的描述,其實都可以看作是對代碼簡寫律杠,上面的代碼在解析時可以當(dāng)做 Ruby 代碼來執(zhí)行潭流。
Fastlane 中的代碼 Fastfile 也是類似的:
lane :beta do
increment_build_number
cocoapods
match
testflight
sh "./customScript.sh"
slack
end
使用描述性的”代碼“編寫腳本竞惋,如果沒有接觸或者使用過 Ruby 的人很難相信上面的這些文本是代碼的。
Ruby 概述
在介紹 CocoaPods 的實現(xiàn)之前灰嫉,我們需要對 Ruby 的一些特性有一個簡單的了解拆宛,在向身邊的朋友“傳教”的時候,我往往都會用優(yōu)雅這個詞來形容這門語言(手動微笑)熬甫。
除了優(yōu)雅之外胰挑,Ruby 的語法具有強大的表現(xiàn)力,并且其使用非常靈活椿肩,能快速實現(xiàn)我們的需求瞻颂,這里簡單介紹一下 Ruby 中的一些特性。
一切皆對象
在許多語言郑象,比如 Java 中贡这,數(shù)字與其他的基本類型都不是對象,而在 Ruby 中所有的元素厂榛,包括基本類型都是對象盖矫,同時也不存在運算符的概念,所謂的 1 + 1
击奶,其實只是 1.+(1)
的語法糖而已辈双。
得益于一切皆對象的概念,在 Ruby 中柜砾,你可以向任意的對象發(fā)送 methods
消息湃望,在運行時自省,所以筆者在每次忘記方法時痰驱,都會直接用 methods
來“查文檔”:
2.3.1 :003 > 1.methods
=> [:%, :&, :*, :+, :-, :/, :<, :>, :^, :|, :~, :-@, :**, :<=>, :<<, :>>, :<=, :>=, :==, :===, :[], :inspect, :size, :succ, :to_s, :to_f, :div, :divmod, :fdiv, :modulo, :abs, :magnitude, :zero?, :odd?, :even?, :bit_length, :to_int, :to_i, :next, :upto, :chr, :ord, :integer?, :floor, :ceil, :round, :truncate, :downto, :times, :pred, :to_r, :numerator, :denominator, :rationalize, :gcd, :lcm, :gcdlcm, :+@, :eql?, :singleton_method_added, :coerce, :i, :remainder, :real?, :nonzero?, :step, :positive?, :negative?, :quo, :arg, :rectangular, :rect, :polar, :real, :imaginary, :imag, :abs2, :angle, :phase, :conjugate, :conj, :to_c, :between?, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :is_a?, :extend, :define_singleton_method, :to_enum, :enum_for, :=~, :!~, :respond_to?, :freeze, :display, :send, :object_id, :method, :public_method, :singleton_method, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]
比如在這里向?qū)ο?1
調(diào)用 methods
就會返回它能響應(yīng)的所有方法证芭。
一切皆對象不僅減少了語言中類型的不一致,消滅了基本數(shù)據(jù)類型與對象之間的邊界担映;這一概念同時也簡化了語言中的組成元素废士,這樣 Ruby 中只有對象和方法,這兩個概念蝇完,這也降低了我們理解這門語言的復(fù)雜度:
- 使用對象存儲狀態(tài)
- 對象之間通過方法通信
block
Ruby 對函數(shù)式編程范式的支持是通過 block官硝,這里的 block 和 Objective-C 中的 block 有些不同。
首先 Ruby 中的 block 也是一種對象短蜕,所有的 Block 都是 Proc 類的實例氢架,也就是所有的 block 都是 first-class 的,可以作為參數(shù)傳遞忿危,返回达箍。
def twice(&proc)
2.times { proc.call() } if proc
end
def twice
2.times { yield } if block_given?
end
yield
會調(diào)用外部傳入的 block,block_given?
用于判斷當(dāng)前方法是否傳入了block
铺厨。
在這個方法調(diào)用時缎玫,是這樣的:
twice do
puts "Hello"
end
eval
最后一個需要介紹的特性就是 eval
了硬纤,早在幾十年前的 Lisp 語言就有了 eval
這個方法,這個方法會將字符串當(dāng)做代碼來執(zhí)行赃磨,也就是說 eval
模糊了代碼與數(shù)據(jù)之間的邊界筝家。
> eval "1 + 2 * 3"
=> 7
有了 eval
方法,我們就獲得了更加強大的動態(tài)能力邻辉,在運行時溪王,使用字符串來改變控制流程,執(zhí)行代碼值骇;而不需要去手動解析輸入莹菱、生成語法樹。
手動解析 Podfile
在我們對 Ruby 這門語言有了一個簡單的了解之后吱瘩,就可以開始寫一個簡易的解析 Podfile 的腳本了道伟。
在這里,我們以一個非常簡單的 Podfile 為例使碾,使用 Ruby 腳本解析 Podfile 中指定的依賴:
source 'http://source.git'
platform :ios, '8.0'
target 'Demo' do
pod 'AFNetworking'
pod 'SDWebImage'
pod 'Masonry'
pod "Typeset"
pod 'BlocksKit'
pod 'Mantle'
pod 'IQKeyboardManager'
pod 'IQDropDownTextField'
end
因為這里的 source
蜜徽、platform
、target
以及 pod
都是方法票摇,所以在這里我們需要構(gòu)建一個包含上述方法的上下文:
# eval_pod.rb
$hash_value = {}
def source(url)
end
def target(target)
end
def platform(platform, version)
end
def pod(pod)
end
使用一個全局變量 hash_value
存儲 Podfile 中指定的依賴拘鞋,并且構(gòu)建了一個 Podfile 解析腳本的骨架;我們先不去完善這些方法的實現(xiàn)細節(jié)矢门,先嘗試一下讀取 Podfile 中的內(nèi)容并執(zhí)行會不會有什么問題盆色。
在 eval_pod.rb
文件的最下面加入這幾行代碼:
content = File.read './Podfile'
eval content
p $hash_value
這里讀取了 Podfile 文件中的內(nèi)容,并把其中的內(nèi)容當(dāng)做字符串執(zhí)行颅和,最后打印 hash_value
的值傅事。
$ ruby eval_pod.rb
運行這段 Ruby 代碼雖然并沒有什么輸出缕允,但是并沒有報出任何的錯誤峡扩,接下來我們就可以完善這些方法了:
def source(url)
$hash_value['source'] = url
end
def target(target)
targets = $hash_value['targets']
targets = [] if targets == nil
targets << target
$hash_value['targets'] = targets
yield if block_given?
end
def platform(platform, version)
end
def pod(pod)
pods = $hash_value['pods']
pods = [] if pods == nil
pods << pod
$hash_value['pods'] = pods
end
在添加了這些方法的實現(xiàn)之后,再次運行腳本就會得到 Podfile 中的依賴信息了障本,不過這里的實現(xiàn)非常簡單的教届,很多情況都沒有處理:
$ ruby eval_pod.rb
{"source"=>"http://source.git", "targets"=>["Demo"], "pods"=>["AFNetworking", "SDWebImage", "Masonry", "Typeset", "BlocksKit", "Mantle", "IQKeyboardManager", "IQDropDownTextField"]}
CocoaPods 中對于 Podfile 的解析與這里的實現(xiàn)其實差不多,接下來就進入了 CocoaPods 的實現(xiàn)部分了驾霜。
CocoaPods 的實現(xiàn)
在上面簡單介紹了 Ruby 的一些語法以及如何解析 Podfile 之后案训,我們開始深入了解一下 CocoaPods 是如何管理 iOS 項目的依賴,也就是 pod install
到底做了些什么粪糙。
Pod install 的過程
pod install
這個命令到底做了什么强霎?首先,在 CocoaPods 中,所有的命令都會由 Command
類派發(fā)到將對應(yīng)的類贝淤,而真正執(zhí)行 pod install
的類就是 Install
:
module Pod
class Command
class Install < Command
def run
verify_podfile_exists!
installer = installer_for_config
installer.repo_update = repo_update?(:default => false)
installer.update = false
installer.install!
end
end
end
end
這里面會從配置類的實例 config
中獲取一個 Installer
的實例妓忍,然后執(zhí)行 install!
方法漫贞,這里的 installer
有一個 update
屬性吁断,而這也就是 pod install
和 update
之間最大的區(qū)別擦俐,其中后者會無視已有的 Podfile.lock 文件佣蓉,重新對依賴進行分析:
module Pod
class Command
class Update < Command
def run
...
installer = installer_for_config
installer.repo_update = repo_update?(:default => true)
installer.update = true
installer.install!
end
end
end
end
Podfile 的解析
Podfile 中依賴的解析其實是與我們在手動解析 Podfile 章節(jié)所介紹的差不多酸舍,整個過程主要都是由 CocoaPods-Core 這個模塊來完成的拉馋,而這個過程早在 installer_for_config
中就已經(jīng)開始了:
def installer_for_config
Installer.new(config.sandbox, config.podfile, config.lockfile)
end
這個方法會從 config.podfile
中取出一個 Podfile
類的實例:
def podfile
@podfile ||= Podfile.from_file(podfile_path) if podfile_path
end
類方法 Podfile.from_file
就定義在 CocoaPods-Core 這個庫中榨为,用于分析 Podfile 中定義的依賴,這個方法會根據(jù) Podfile 不同的類型選擇不同的調(diào)用路徑:
Podfile.from_file
`-- Podfile.from_ruby
|-- File.open
`-- eval
from_ruby
類方法就會像我們在前面做的解析 Podfile 的方法一樣煌茴,從文件中讀取數(shù)據(jù)随闺,然后使用 eval
直接將文件中的內(nèi)容當(dāng)做 Ruby 代碼來執(zhí)行。
def self.from_ruby(path, contents = nil)
contents ||= File.open(path, 'r:utf-8', &:read)
podfile = Podfile.new(path) do
begin
eval(contents, nil, path.to_s)
rescue Exception => e
message = "Invalid `#{path.basename}` file: #{e.message}"
raise DSLError.new(message, path, e, contents)
end
end
podfile
end
在 Podfile 這個類的頂部蔓腐,我們使用 Ruby 的 Mixin
的語法來混入 Podfile 中代碼執(zhí)行所需要的上下文:
include Pod::Podfile::DSL
Podfile 中的所有你見到的方法都是定義在 DSL
這個模塊下面的:
module Pod
class Podfile
module DSL
def pod(name = nil, *requirements) end
def target(name, options = nil) end
def platform(name, target = nil) end
def inhibit_all_warnings! end
def use_frameworks!(flag = true) end
def source(source) end
...
end
end
end
這里定義了很多 Podfile 中使用的方法板壮,當(dāng)使用 eval
執(zhí)行文件中的代碼時,就會執(zhí)行這個模塊里的方法合住,在這里簡單看一下其中幾個方法的實現(xiàn)绰精,比如說 source
方法:
def source(source)
hash_sources = get_hash_value('sources') || []
hash_sources << source
set_hash_value('sources', hash_sources.uniq)
end
該方法會將新的 source
加入已有的源數(shù)組中,然后更新原有的 sources
對應(yīng)的值透葛。
稍微復(fù)雜一些的是 target
方法:
def target(name, options = nil)
if options
raise Informative, "Unsupported options `#{options}` for " \
"target `#{name}`."
end
parent = current_target_definition
definition = TargetDefinition.new(name, parent)
self.current_target_definition = definition
yield if block_given?
ensure
self.current_target_definition = parent
end
這個方法會創(chuàng)建一個 TargetDefinition
類的實例笨使,然后將當(dāng)前環(huán)境系的 target_definition
設(shè)置成這個剛剛創(chuàng)建的實例。這樣僚害,之后使用 pod
定義的依賴都會填充到當(dāng)前的 TargetDefinition
中:
def pod(name = nil, *requirements)
unless name
raise StandardError, 'A dependency requires a name.'
end
current_target_definition.store_pod(name, *requirements)
end
當(dāng) pod
方法被調(diào)用時硫椰,會執(zhí)行 store_pod
將依賴存儲到當(dāng)前 target
中的 dependencies
數(shù)組中:
def store_pod(name, *requirements)
return if parse_subspecs(name, requirements)
parse_inhibit_warnings(name, requirements)
parse_configuration_whitelist(name, requirements)
if requirements && !requirements.empty?
pod = { name => requirements }
else
pod = name
end
get_hash_value('dependencies', []) << pod
nil
end
總結(jié)一下,CocoaPods 對 Podfile 的解析與我們在前面做的手動解析 Podfile 的原理差不多萨蚕,構(gòu)建一個包含一些方法的上下文靶草,然后直接執(zhí)行 eval
方法將文件的內(nèi)容當(dāng)做代碼來執(zhí)行,這樣只要 Podfile 中的數(shù)據(jù)是符合規(guī)范的岳遥,那么解析 Podfile 就是非常簡單容易的奕翔。
安裝依賴的過程
Podfile 被解析后的內(nèi)容會被轉(zhuǎn)化成一個 Podfile
類的實例,而 Installer
的實例方法 install!
就會使用這些信息安裝當(dāng)前工程的依賴浩蓉,而整個安裝依賴的過程大約有四個部分:
- 解析 Podfile 中的依賴
- 下載依賴
- 創(chuàng)建
Pods.xcodeproj
工程 - 集成 workspace
def install!
resolve_dependencies
download_dependencies
generate_pods_project
integrate_user_project
end
在上面的 install
方法調(diào)用的 resolve_dependencies
會創(chuàng)建一個 Analyzer
類的實例派继,在這個方法中,你會看到一些非常熟悉的字符串:
def resolve_dependencies
analyzer = create_analyzer
plugin_sources = run_source_provider_hooks
analyzer.sources.insert(0, *plugin_sources)
UI.section 'Updating local specs repositories' do
analyzer.update_repositories
end if repo_update?
UI.section 'Analyzing dependencies' do
analyze(analyzer)
validate_build_configurations
clean_sandbox
end
end
在使用 CocoaPods 中經(jīng)常出現(xiàn)的 Updating local specs repositories
以及 Analyzing dependencies
就是從這里輸出到終端的捻艳,該方法不僅負責(zé)對本地所有 PodSpec 文件的更新驾窟,還會對當(dāng)前 Podfile
中的依賴進行分析:
def analyze(analyzer = create_analyzer)
analyzer.update = update
@analysis_result = analyzer.analyze
@aggregate_targets = analyzer.result.targets
end
analyzer.analyze
方法最終會調(diào)用 Resolver
的實例方法 resolve
:
def resolve
dependencies = podfile.target_definition_list.flat_map do |target|
target.dependencies.each do |dep|
@platforms_by_dependency[dep].push(target.platform).uniq! if target.platform
end
end
@activated = Molinillo::Resolver.new(self, self).resolve(dependencies, locked_dependencies)
specs_by_target
rescue Molinillo::ResolverError => e
handle_resolver_error(e)
end
這里的 Molinillo::Resolver
就是用于解決依賴關(guān)系的類。
解決依賴關(guān)系(Resolve Dependencies)
CocoaPods 為了解決 Podfile 中聲明的依賴關(guān)系认轨,使用了一個叫做 Milinillo 的依賴關(guān)系解決算法绅络;但是,筆者在 Google 上并沒有找到與這個算法相關(guān)的其他信息,推測是 CocoaPods 為了解決 iOS 中的依賴關(guān)系創(chuàng)造的算法恩急。
Milinillo 算法的核心是 回溯(Backtracking) 以及 向前檢查(forward check)节视,整個過程會追蹤棧中的兩個狀態(tài)(依賴和可能性)。
在這里并不想陷入對這個算法執(zhí)行過程的分析之中假栓,如果有興趣可以看一下倉庫中的 ARCHITECTURE.md 文件寻行,其中比較詳細的解釋了 Milinillo 算法的工作原理,并對其功能執(zhí)行過程有一個比較詳細的介紹匾荆。
Molinillo::Resolver
方法會返回一個依賴圖拌蜘,其內(nèi)容大概是這樣的:
Molinillo::DependencyGraph:[
Molinillo::DependencyGraph::Vertex:AFNetworking(#<Pod::Specification name="AFNetworking">),
Molinillo::DependencyGraph::Vertex:SDWebImage(#<Pod::Specification name="SDWebImage">),
Molinillo::DependencyGraph::Vertex:Masonry(#<Pod::Specification name="Masonry">),
Molinillo::DependencyGraph::Vertex:Typeset(#<Pod::Specification name="Typeset">),
Molinillo::DependencyGraph::Vertex:CCTabBarController(#<Pod::Specification name="CCTabBarController">),
Molinillo::DependencyGraph::Vertex:BlocksKit(#<Pod::Specification name="BlocksKit">),
Molinillo::DependencyGraph::Vertex:Mantle(#<Pod::Specification name="Mantle">),
...
]
這個依賴圖是由一個結(jié)點數(shù)組組成的,在 CocoaPods 拿到了這個依賴圖之后牙丽,會在 specs_by_target
中按照 Target
將所有的 Specification
分組:
{
#<Pod::Podfile::TargetDefinition label=Pods>=>[],
#<Pod::Podfile::TargetDefinition label=Pods-Demo>=>[
#<Pod::Specification name="AFNetworking">,
#<Pod::Specification name="AFNetworking/NSURLSession">,
#<Pod::Specification name="AFNetworking/Reachability">,
#<Pod::Specification name="AFNetworking/Security">,
#<Pod::Specification name="AFNetworking/Serialization">,
#<Pod::Specification name="AFNetworking/UIKit">,
#<Pod::Specification name="BlocksKit/Core">,
#<Pod::Specification name="BlocksKit/DynamicDelegate">,
#<Pod::Specification name="BlocksKit/MessageUI">,
#<Pod::Specification name="BlocksKit/UIKit">,
#<Pod::Specification name="CCTabBarController">,
#<Pod::Specification name="CategoryCluster">,
...
]
}
而這些 Specification
就包含了當(dāng)前工程依賴的所有第三方框架简卧,其中包含了名字、版本烤芦、源等信息举娩,用于依賴的下載。
下載依賴
在依賴關(guān)系解決返回了一系列 Specification
對象之后构罗,就到了 Pod install 的第二部分铜涉,下載依賴:
def install_pod_sources
@installed_specs = []
pods_to_install = sandbox_state.added | sandbox_state.changed
title_options = { :verbose_prefix => '-> '.green }
root_specs.sort_by(&:name).each do |spec|
if pods_to_install.include?(spec.name)
if sandbox_state.changed.include?(spec.name) && sandbox.manifest
previous = sandbox.manifest.version(spec.name)
title = "Installing #{spec.name} #{spec.version} (was #{previous})"
else
title = "Installing #{spec}"
end
UI.titled_section(title.green, title_options) do
install_source_of_pod(spec.name)
end
else
UI.titled_section("Using #{spec}", title_options) do
create_pod_installer(spec.name)
end
end
end
end
在這個方法中你會看到更多熟悉的提示,CocoaPods 會使用沙盒(sandbox)存儲已有依賴的數(shù)據(jù)遂唧,在更新現(xiàn)有的依賴時芙代,會根據(jù)依賴的不同狀態(tài)顯示出不同的提示信息:
-> Using AFNetworking (3.1.0)
-> Using AKPickerView (0.2.7)
-> Using BlocksKit (2.2.5) was (2.2.4)
-> Installing MBProgressHUD (1.0.0)
...
雖然這里的提示會有三種,但是 CocoaPods 只會根據(jù)不同的狀態(tài)分別調(diào)用兩種方法:
install_source_of_pod
create_pod_installer
create_pod_installer
方法只會創(chuàng)建一個 PodSourceInstaller
的實例盖彭,然后加入 pod_installers
數(shù)組中纹烹,因為依賴的版本沒有改變,所以不需要重新下載召边,而另一個方法的 install_source_of_pod
的調(diào)用棧非常龐大:
installer.install_source_of_pod
|-- create_pod_installer
| `-- PodSourceInstaller.new
`-- podSourceInstaller.install!
`-- download_source
`-- Downloader.download
`-- Downloader.download_request
`-- Downloader.download_source
|-- Downloader.for_target
| |-- Downloader.class_for_options
| `-- Git/HTTP/Mercurial/Subversion.new
|-- Git/HTTP/Mercurial/Subversion.download
`-- Git/HTTP/Mercurial/Subversion.download!
`-- Git.clone
在調(diào)用棧的末端 Downloader.download_source
中執(zhí)行了另一個 CocoaPods 組件 CocoaPods-Download 中的方法:
def self.download_source(target, params)
FileUtils.rm_rf(target)
downloader = Downloader.for_target(target, params)
downloader.download
target.mkpath
if downloader.options_specific?
params
else
downloader.checkout_options
end
end
方法中調(diào)用的 for_target
根據(jù)不同的源會創(chuàng)建一個下載器铺呵,因為依賴可能通過不同的協(xié)議或者方式進行下載,比如說 Git/HTTP/SVN 等等隧熙,組件 CocoaPods-Downloader 就會根據(jù) Podfile 中依賴的參數(shù)選項使用不同的方法下載依賴片挂。
大部分的依賴都會被下載到 ~/Library/Caches/CocoaPods/Pods/Release/
這個文件夾中,然后從這個這里復(fù)制到項目工程目錄下的 ./Pods
中贱鼻,這也就完成了整個 CocoaPods 的下載流程宴卖。
生成 Pods.xcodeproj
CocoaPods 通過組件 CocoaPods-Downloader 已經(jīng)成功將所有的依賴下載到了當(dāng)前工程中滋将,這里會將所有的依賴打包到 Pods.xcodeproj
中:
def generate_pods_project(generator = create_generator)
UI.section 'Generating Pods project' do
generator.generate!
@pods_project = generator.project
run_podfile_post_install_hooks
generator.write
generator.share_development_pod_schemes
write_lockfiles
end
end
generate_pods_project
中會執(zhí)行 PodsProjectGenerator
的實例方法 generate!
:
def generate!
prepare
install_file_references
install_libraries
set_target_dependencies
end
這個方法做了幾件小事:
- 生成
Pods.xcodeproj
工程 - 將依賴中的文件加入工程
- 將依賴中的 Library 加入工程
- 設(shè)置目標(biāo)依賴(Target Dependencies)
這幾件事情都離不開 CocoaPods 的另外一個組件 Xcodeproj邻悬,這是一個可以操作一個 Xcode 工程中的 Group 以及文件的組件,我們都知道對 Xcode 工程的修改大多數(shù)情況下都是對一個名叫 project.pbxproj
的文件進行修改随闽,而 Xcodeproj 這個組件就是 CocoaPods 團隊開發(fā)的用于操作這個文件的第三方庫父丰。
生成 workspace
最后的這一部分與生成 Pods.xcodeproj
的過程有一些相似,這里使用的類是 UserProjectIntegrator
,調(diào)用方法 integrate!
時蛾扇,就會開始集成工程所需要的 Target:
def integrate!
create_workspace
integrate_user_targets
warn_about_xcconfig_overrides
save_projects
end
對于這一部分的代碼攘烛,也不是很想展開來細談,簡單介紹一下這里的代碼都做了什么镀首,首先會通過 Xcodeproj::Workspace
創(chuàng)建一個 workspace坟漱,之后會獲取所有要集成的 Target 實例,調(diào)用它們的 integrate!
方法:
def integrate!
UI.section(integration_message) do
XCConfigIntegrator.integrate(target, native_targets)
add_pods_library
add_embed_frameworks_script_phase
remove_embed_frameworks_script_phase_from_embedded_targets
add_copy_resources_script_phase
add_check_manifest_lock_script_phase
end
end
方法將每一個 Target 加入到了工程更哄,使用 Xcodeproj 修改 Copy Resource Script Phrase
等設(shè)置芋齿,保存 project.pbxproj
,整個 Pod install 的過程就結(jié)束了成翩。
總結(jié)
最后想說的是 pod install 和 pod update 區(qū)別還是比較大的觅捆,每次在執(zhí)行 pod install 或者 update 時最后都會生成或者修改 Podfile.lock
文件,其中前者并不會修改 Podfile.lock
中顯示指定的版本麻敌,而后者會會無視該文件的內(nèi)容栅炒,嘗試將所有的 pod 更新到最新版。
CocoaPods 工程的代碼雖然非常多术羔,不過代碼的邏輯非常清晰赢赊,整個管理并下載依賴的過程非常符合直覺以及邏輯。
其它
Github Repo:iOS-Source-Code-Analyze
Follow: Draveness · GitHub
Source: http://draveness.me/cocoapods