CocoaPods 歷險記 這個專題是 Edmond 和 冬瓜 共同撰寫,對於 iOS / macOS 工程中版本管理工具 CocoaPods 的實現細節、原理、源碼、實踐與經驗的分享記錄,旨在幫助你們可以更加了解這個依賴管理工具,而不只侷限於
pod install
和pod update
。html
本文知識目錄
引子
在上文 「CocoaPods 命令解析 - CLAide」 中,咱們經過對 CLAide 的源碼分析,瞭解了 CocoaPods 是如何處理 pod
命令,多級命令又是如何組織和嵌套的,並解釋了命令行輸出所表明的含義。今天咱們開始學習 Podfile
。ios
大多 iOS 工程師最早接觸到的 CocoaPods 概念應該是 Podfile
,而 Podfile
屬於 cocoapods-core
(如下簡稱 Core) 的兩大概念之一。另一個則是 Podspec[2] (用於描述 Pod Library 的配置文件),只有當你須要開發 Pod 組件的時候纔會接觸。git
在介紹 Podfile 的內容結構以前,必需要談談 Xcode 的工程結構。github
Xcode 工程結構
咱們先來看一個極簡 Podfile 聲明:web
target 'Demo' do
pod 'Alamofire', :path => './Alamofire'
end
它編譯後的工程目錄以下:算法
如你所見 Podfile 的配置是圍繞 Xcode 的這些工程結構:Workspace、Project、Target 以及 Build Setting 來展開的。做爲包管理工具 CocoaPods 將所管理的 Pods 依賴庫組裝成一個個 Target,統一放入 Pods project
中的 Demo target
,並自動配置好 Target 間的依賴關係。swift
以後將 Example
主工程和 Pods
工程一塊兒打包到新建的 Example.workspace
,配好主工程與 Pods
工程之間的依賴,完成最終轉換。vim
接下來,咱們來聊一聊這些 Xcode 結構:數組
Target - 最小可編譯單元
首先是 Target,它做爲工程中最小的可編譯單元,根據 Build Phases[3] 和 Build Settings[4] 將源碼做爲輸入,經編譯後輸出結果產物。xcode
其輸出結果能夠是連接庫、可執行文件或者資源包等,具體細節以下:
-
Build Setting:好比指定使用的編譯器,目標平臺、編譯參數、頭文件搜索路徑等; -
Build 時的前置依賴、執行的腳本文件; -
Build 生成目標的簽名、Capabilities 等屬性; -
Input:哪些源碼或者資源文件會被編譯打包; -
Output:哪些靜態庫、動態庫會被連接;
Project - Targets 的載體
Project 就是一個獨立的 Xcode 工程,做爲一個或多個 Targets 的資源管理器,自己沒法被編譯。Project 所管理的資源都來自它所包含的 Targets。特色以下:
-
至少包含一個或多個可編譯的 Target; -
爲所包含的 Targets 定義了一份默認編譯選項,若是 Target 有本身的配置,則會覆蓋 Project 的預設值; -
能將其餘 Project 做爲依賴嵌入其中;
下圖爲 Project 與所包含對 Targets 的關係
Workspace - 容器
做爲純粹的項目容器,Workspace 不參與任何編譯連接過程,僅用於管理同層級的 Project,其特色:
-
Workspace 能夠包含多個 Projects; -
同一個 Workspace 中的 Proejct 文件對於其餘 Project 是默承認見的, 這些 Projcts 會共享 workspace build directory
; -
一個 Xcode Project 能夠被包含在多個不一樣的 Workspace 中,由於每一個 Project 都有獨立的 Identity,默認是 Project Name;
Scheme - 描述 Build 過程
Scheme 是對於整個 Build 過程的一個抽象,它描述了 Xcode 應該使用哪一種 Build Configurations[5] 、執行什麼任務、環境參數等來構建咱們所需的 Target。
Scheme 中預設了六個主要過程:Build、Run、Test、Profile、Analyze、Archive。包括了咱們對 Target 的全部操做,每個過程均可以單獨配置。
CocoaPods-Core
CocoaPods-Core 用於 CocoaPods 中配置文件的解析,包括 Podfile
、Podspec
以及解析後的依賴鎖存文件,如 Podfile.lock 等。
CocoaPods-Core 的文件構成
照例,咱們先經過入口文件 lib/cocoapods-core.rb
來一窺 Core 項目的主要文件:
module Pod
require 'cocoapods-core/gem_version'
class PlainInformative < StandardError; end
class Informative < PlainInformative; end
require 'pathname'
require 'cocoapods-core/vendor'
# 用於存儲 PodSpec 中的版本號
autoload :Version, 'cocoapods-core/version'
# pod 的版本限制
autoload :Requirement, 'cocoapods-core/requirement'
# 配置 Podfile 或 PodSpec 中的 pod 依賴
autoload :Dependency, 'cocoapods-core/dependency'
# 獲取 Github 倉庫信息
autoload :GitHub, 'cocoapods-core/github'
# 處理 HTTP 請求
autoload :HTTP, 'cocoapods-core/http'
# 記錄最終 pod 的依賴信息
autoload :Lockfile, 'cocoapods-core/lockfile'
# 記錄 SDK 的名稱和 target 版本
autoload :Platform, 'cocoapods-core/platform'
# 對應 Podfile 文件的 class
autoload :Podfile, 'cocoapods-core/podfile'
# 管理 PodSpec 的集合
autoload :Source, 'cocoapods-core/source'
# 管理基於 CDN 來源的 PodSpec 集合
autoload :CDNSource, 'cocoapods-core/cdn_source'
# 管理基於 Trunk 來源的 PodSpec 集合
autoload :TrunkSource, 'cocoapods-core/trunk_source'
# 對應 PodSpec 文件的 class
autoload :Specification, 'cocoapods-core/specification'
# 將 pod 信息轉爲 .yml 文件,用於 lockfile 的序列化
autoload :YAMLHelper, 'cocoapods-core/yaml_helper'
# 記錄 pod 依賴類型,是靜態庫/動態庫
autoload :BuildType, 'cocoapods-core/build_type'
...
Spec = Specification
end
將這些 Model 類按照對應的依賴關係進行劃分,層級以下:
Podfile 的主要數據結構
先來了解 Podfile 的主要數據結構
Specification
Specification 即存儲 PodSpec
的內容,是用於描述一個 Pod 庫的源代碼和資源將如何被打包編譯成連接庫或 framework,後續將會介紹更多的細節。
TargetDefinition
TargetDefinition
是一個多叉樹結構,每一個節點記錄着 Podfile
中定義的 Pod 的 Source 來源、Build Setting、Pod 子依賴等。
該樹的根節點指向 Podfile
,而 Podfile
中的 root_target_definitions
則記錄着全部的 TargetDefinition
的根節點,正常狀況下該 list 中只有一個 root 即 Pods.project
。
爲了便於閱讀,簡化了大量的 DSL 配置相關的方法和屬性並對代碼順序作了調整,大體結構以下:
module Pod
class Podfile
class TargetDefinition
# 父節點: TargetDefinition 或者 Podfile
attr_reader :parent
# 子節點: TargetDefinition
attr_reader :children
# 記錄 tareget 的配置信息
attr_accessor :internal_hash
def root?
parent.is_a?(Podfile) || parent.nil?
end
def root
if root?
self
else
parent.root
end
end
def podfile
root.parent
end
# ...
end
end
end
對應上一節 Xcode 工程結構中的 Podfile
關係以下:
CocoaPods 正是巧妙利用了 Xcode 工程結構的特色,引入 Pods.project
這一中間層,將主工程的 Pods 依賴所有轉接到 Pods.project
上,最後再將 Pods.project
做爲主項目的依賴。
儘管這麼作也受到了一些質疑和詬病(所謂的侵入性太強),但筆者的觀點是,正得益於 Pods.project
這一設計隔絕了第三方依賴庫對於主項目的頻繁更改,也便於後續的管理和更新,體現了軟件工程中的 開放-關閉原則 。
好比,在 Pod 1.7.0 版本中支持的 Multiple Xcodeproj Generation[6] 就是解決隨着項目的迭代而日益增大的 Pods
project 的問題。
試想當你的項目中存在上百個依賴庫,每一個依賴庫的變動都會影響到你的主工程,這將是很是可怕的問題。
Podfile
Podfile
是用於描述一個或多個 Xcode Project 中各個 Targets 之間的依賴關係。
這些 Targets 的依賴關係對應的就是 TargetDefinition
樹中的各子節點的層級關係。如前面所說,有了 Podfile
這個根節點的指向,僅需對依賴樹進行遍歷,就能輕鬆獲取完整的依賴關係。
有了這層依賴樹,對於某個 Pod
庫的更新便是對樹節點的更新,即可輕鬆的分析出這次更新涉及的影響。
簡化調整後的 Podfile 代碼以下:
require 'cocoapods-core/podfile/dsl'
require 'cocoapods-core/podfile/target_definition'
module Pod
class Podfile
include Pod::Podfile::DSL
# podfile 路徑
attr_accessor :defined_in_file
# 全部的 TargetDefinition 的根節點, 正常只有一個,即 Pods.project target
attr_accessor :root_target_definitions
# 記錄 Pods.project 項目的配置信息
attr_accessor :internal_hash
# 當前 DSL 解析使用的 TargetDefinition
attr_accessor :current_target_definition
# ...
end
end
直接看 dsl.rb
,該文件內部定義了 Podfile DSL 支持的全部方法。經過 include 的使用將 Pod::Podfile::DSL
模塊 Mix-in 後插入到 Podfile 類中。想了解更多 Mix-in 特性,移步 「CocoaPods 中的 Ruby 特性之 Mix-in」。
Lockfile
Lockfile
,顧名思義是用於記錄最後一次 CocoaPods 所安裝的 Pod 依賴庫版本的信息快照。也就是生成的 Podfile.lock
。
在 pod install
過程,Podfile 會結合它來確認最終所安裝的 Pod 版本,固定 Pod 依賴庫版本防止其自動更新。
Lockfile
也做爲 Pods 狀態清單 (mainfest
),用於記錄安裝過程的中哪些 Pod 須要被刪除或安裝或更新等。
以開頭的 Podfile 經 pod install
所生成的 Podfile.lock
爲例:
PODS:
- Alamofire (4.6.0)
DEPENDENCIES:
- Alamofire (from `./Alamofire`)
EXTERNAL SOURCES:
Alamofire:
:path: "./Alamofire"
SPEC CHECKSUMS:
Alamofire: 0dda98a0ed7eec4bdcd5fe3cdd35fcd2b3022825
PODFILE CHECKSUM: da12cc12a30cfb48ebc5d14e8f51737ab65e8241
COCOAPODS: 1.10.0.beta.2
咱們來分析一下,經過該 Lockfile 可以獲取哪些信息:
Key | 含義 |
---|---|
PODS | 記錄全部 Pod 庫的具體安裝版本號 |
DEPENDENCIES | 記錄各 Pod 庫之間的相互依賴關係,因爲這裏只有 Alamofire 且它無其餘依賴,暫時無關看出區別 |
EXTERNAL SOURCES | 記錄部分經過外部源的 Pod 庫(Git 引入、Path 引入) |
SPEC CHECKSUMS | 記錄當前各 Pod 庫的 Podspec 文件 Hash 值,其實就是文件的 md5 |
PODFILE CHECKSUM | 記錄 Podfile 文件的 Hash 值,一樣是 md5,確認是否有變動 |
COCOAPODS | 記錄上次所使用的 CocoaPods 版本 |
Podfile
內容加載
Podfile
文件類型
你能夠在 CocoaPods 的 /lib/cocoapods/config.rb
找到 Podfile 所支持的文件類型:
PODFILE_NAMES = [
'CocoaPods.podfile.yaml',
'CocoaPods.podfile',
'Podfile',
'Podfile.rb',
].freeze
CocoaPods 按照上述命名優先級來查找工程目錄下所對應的 Podfile
文件。當發現目錄中存在 CocoaPods.podfile.yaml
文件時會優先加載。
不少同窗可能只知道到 Podfile 支持 Ruby 的文件格式,而不瞭解它還支持了 YAML 格式。YAML 是 YAML Ain't Markup Language
的縮寫,其官方定義[7]以下:
它是一種面向工程師友好的序列化語言。咱們的 Lockfile 文件就是以 YAML 格式寫入 Podfile.lock
中的。
Podfile 文件讀取
回到 lib/cocoapods-core/podfile.rb
來看讀取方法:
module Pod
class Podfile
include Pod::Podfile::DSL
def self.from_file(path)
path = Pathname.new(path)
unless path.exist?
raise Informative, "No Podfile exists at path `#{path}`."
end
# 這裏咱們能夠看出,Podfile 目前已經支持告終尾是 .podfile 和 .rb 後綴的文件名
# 實際上是爲了改善不少編譯器使用文件後綴來確認 filetype,好比 vim
# 相比與 Podfile 這個文件名要更加的友好
case path.extname
when '', '.podfile', '.rb'
Podfile.from_ruby(path)
when '.yaml'
# 如今也支持了 .yaml 格式
Podfile.from_yaml(path)
else
raise Informative, "Unsupported Podfile format `#{path}`."
end
end
end
from_file
在 pod install
命令執行後的 verify_podfile_exists!
中被調用的:
def verify_podfile_exists!
unless config.podfile
raise Informative, "No `Podfile' found in the project directory."
end
end
而 Podfile
文件的讀取就是 config.podfile
裏觸發的,代碼在 CocoaPods 的 config.rb
文件中:
def podfile_path_in_dir(dir)
PODFILE_NAMES.each do |filename|
candidate = dir + filename
if candidate.file?
return candidate
end
end
nil
end
def podfile_path
@podfile_path ||= podfile_path_in_dir(installation_root)
end
def podfile
@podfile ||= Podfile.from_file(podfile_path) if podfile_path
end
這裏的方法 podfile
和 podfile_path
都是 lazy 加載的。最後 Core 的 from_file
將依據目錄下的 Podfile
文件類型選擇調用 from_yaml
或者 from_ruby
。
從 Pod::Command::Install
命令到 Podfile 文件加載的調用棧以下:
Podfile From Ruby 解析
當咱們經過 pod init
來初始化 CocoaPods 項目時,默認生成的 Podfile 名稱就是 Podfile
,那就從 Podfile.from_ruby
開始。
def self.from_ruby(path, contents = nil)
# ①
contents ||= File.open(path, 'r:utf-8', &:read)
# 兼容 1.9 版本的 Rubinius 中的編碼問題
if contents.respond_to?(:encoding) && contents.encoding.name != 'UTF-8'
contents.encode!('UTF-8')
end
# 對 Podfile 中不規範的單引號或雙引號進行檢查,並進行自動修正,及拋出錯誤
if contents.tr!('「」‘’‛', %(""'''))
CoreUI.warn "..."
end
# ②
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 內容的讀取和編碼,同時對可能出現的單引號和雙引號的匹配問題進行了修正。② 以 path
和 block
爲入參進行 podfile
類的初始化並將其放回,保存在全局的 config.podfile
中。
Tips: 若是要在 Ruby 對象的初始化中傳入參數,須要重載 Object 的 initialize[8] 方法,這裏的 Podfile.new(...) 本質上是
initialize
的方法調用。
initialize
方法所傳入的尾隨閉包 block
的核心在於內部的 eval
函數(在 CocoaPods 核心組件[9] 中有提到):
eval(contents, nil, path.to_s)
它將 Podfile 中的文本內容轉化爲方法執行,也就是說裏面的參數是一段 Ruby 的代碼字符串,經過 eval
方法能夠直接執行。繼續看 Podfile 的 initialize
方法:
def initialize(defined_in_file = nil, internal_hash = {}, &block)
self.defined_in_file = defined_in_file
@internal_hash = internal_hash
if block
default_target_def = TargetDefinition.new('Pods', self)
default_target_def.abstract = true
@root_target_definitions = [default_target_def]
@current_target_definition = default_target_def
instance_eval(&block)
else
@root_target_definitions = []
end
end
它定義了三個參數:
參數 | 定義 |
---|---|
defined_in_file |
Podfile 文件路徑 |
internal_hash |
經過 yaml 序列化獲得的 Podfile 配置信息,保存在 internal_hash 中 |
block |
用於映射 Podfile 的 DSL 配置 |
須要注意的是,經過
from_ruby
初始化的Podfile
只傳入了參數 1 和 3,參數 2internal_hash
則是提供給from_yaml
的。
當 block
存在,會初始化名爲 Pods
的 TargetDefinition 對象,用於保存 Pods project
的相關信息和 Pod 依賴。而後調用 instance_eval[10] 執行傳入的 block
,將 Podfile 的 DSL 內容轉換成對應的方法和參數,最終將參數存入 internal_hash
和對應的 target_definitions
中。
Tips: 在 Ruby 中存在兩種不一樣的方式來執行代碼塊
block
,分別是instance_eval
和class_eval
。class_eval
的執行上下文與調用類相關,調用者是類名或者模塊名,而instance_eval
的調用者能夠是類的實例或者類自己。細節看 StackOverFlow[11]。
Podfile From YAML 解析
YAML 格式的 Podfile 加載須要藉助 YAMLHelper 類來完成,YAMLHelper 則是基於 yaml[12] 的簡單封裝。
def self.from_yaml(path)
string = File.open(path, 'r:utf-8', &:read)
# 爲了解決 Rubinius incomplete encoding in 1.9 mode
# https://github.com/rubinius/rubinius/issues/1539
if string.respond_to?(:encoding) && string.encoding.name != 'UTF-8'
string.encode!('UTF-8')
end
hash = YAMLHelper.load_string(string)
from_hash(hash, path)
end
def self.from_hash(hash, path = nil)
internal_hash = hash.dup
target_definitions = internal_hash.delete('target_definitions') || []
podfile = Podfile.new(path, internal_hash)
target_definitions.each do |definition_hash|
definition = TargetDefinition.from_hash(definition_hash, podfile)
podfile.root_target_definitions << definition
end
podfile
end
經過 from_yaml
將文件內容轉成 Ruby hash 後轉入 from_hash
方法。
區別於 from_ruby
,這裏調用的 initialize
將讀取的 hash 直接存入 internal_hash
,而後利用 TargetDefinition.from_hash
來完成的 hash 內容到 targets 的轉換,所以,這裏無需傳入 block 進行 DSL 解析和方法轉換。
Podfile 內容解析
前面提到 Podfile 的內容最終保存在 internal_hash
和 target_definitions
中,本質上都是使用了 hash
來保存數據。因爲 YAML 文件格式的 Podfile 加載後就是 hash 對象,無需過多加工。惟一須要處理的是遞歸調用 TargetDefinition 的 from_hash
方法來解析 target 子節點的數據。
所以,接下來的內容解析主要針對 Ruby 文件格式的 DSL 解析,咱們以 pod
方法爲例:
target 'Example' do
pod 'Alamofire'
end
當解析到 pod 'Alamofire'
時,會先經過 eval(contents, nil, path.to_s
將其轉換爲 dsl.rb
中的方法:
def pod(name = nil, *requirements)
unless name
raise StandardError, 'A dependency requires a name.'
end
current_target_definition.store_pod(name, *requirements)
end
name 爲 Alamofire,因爲咱們沒有指定對應的 Alamofire 版本,默認會使用最新版本。requirements
是控制 該 pod 來源獲取或者 pod target 的編譯選項等,例如:
pod 'Alamofire', '0.9'
pod 'Alamofire', :modular_headers => true
pod 'Alamofire', :configurations => ['Debug', 'Beta']
pod 'Alamofire', :source => 'https://github.com/CocoaPods/Specs.git'
pod 'Alamofire', :subspecs => ['Attribute', 'QuerySet']
pod 'Alamofire', :testspecs => ['UnitTests', 'SomeOtherTests']
pod 'Alamofire', :path => '~/Documents/AFNetworking'
pod 'Alamofire', :podspec => 'https://example.com/Alamofire.podspec'
pod 'Alamofire', :git => 'https://github.com/looseyi/Alamofire.git', :tag => '0.7.0'
Tips:requirements 最終是以 Gem::Requirement 對象來保存的。關於 pod 詳細說明請移步:Podfile 手冊[13]。
對 name 進行校驗後,直接轉入 current_target_definition
畢竟 Pod 庫都是存在 Pods.project
之下:
def store_pod(name, *requirements)
return if parse_subspecs(name, requirements) # This parse method must be called first
parse_inhibit_warnings(name, requirements)
parse_modular_headers(name, requirements)
parse_configuration_whitelist(name, requirements)
parse_project_name(name, requirements)
if requirements && !requirements.empty?
pod = { name => requirements }
else
pod = name
end
get_hash_value('dependencies', []) << pod
nil
end
def get_hash_value(key, base_value = nil)
unless HASH_KEYS.include?(key)
raise StandardError, "Unsupported hash key `#{key}`"
end
internal_hash[key] = base_value if internal_hash[key].nil?
internal_hash[key]
end
def set_hash_value(key, value)
unless HASH_KEYS.include?(key)
raise StandardError, "Unsupported hash key `#{key}`"
end
internal_hash[key] = value
end
通過一系列檢查以後,調用 get_hash_value
獲取 internal_hash
的 dependencies
,並將 name 和 requirements
選項存入。
這裏的 dependencies
key 是定義在 TargetDefinition 文件的 HASH_KEYS
,表示 Core 所支持的配置參數:
# freeze 表示該數組不可修改。另外,%w 用於表示其中元素被單引號括起的數組。
# %W(#{foo} Bar Bar\ with\ space) => ["Foo", "Bar", "Bar with space"]
# 對應的還有 %W 表示其中元素被雙引號括起的數組。
HASH_KEYS = %w(
name
platform
podspecs
exclusive
link_with
link_with_first_target
inhibit_warnings
use_modular_headers
user_project_path
build_configurations
project_names
dependencies
script_phases
children
configuration_pod_whitelist
uses_frameworks
swift_version_requirements
inheritance
abstract
swift_version
).freeze
整個映射過程以下:
精細化的 Podfile 配置
最後一節讓咱們來 Show 一下 ,看看 Podfile
所謂的 targets
之間的依賴關係能夠玩出什麼花來 😂 。
Target 嵌套
最簡單的 Podfile
就是文章開頭所展現的,不過在 Podfile
中還能夠對 Target 進行嵌套使用。
假設在咱們的主工程同時維護了三個項目,它們都依賴了 Alamofire,經過俄羅斯套娃就能輕鬆知足條件:
target 'Demo1' do
pod 'Alamofire'
target 'Demo2' do
target 'Demo3' do
end
end
end
編譯後的 Pods.project
項目結構以下:
咱們知道,CocoaPods 在 Pods.project
中爲每一個在 Podfile 中聲明的 Target 生成一個與之對應的專屬 Target 來集成它的 Pod 依賴。
對於有依賴關係的 Target 其生成的專屬 Target 名稱則會按照依賴關係疊加來命名,如 target Demo3
的專屬 Target 名稱爲 Pods-Demo1-Demo2-Demo3。安裝完成後主項目將會引入該專屬 Target 來完成依賴關聯,如 Demo3:
關於 Target 嵌套,一個父節點是能夠有多個子節點的:
target 'Demo1' do
pod 'Alamofire'
target 'Demo2' do
pod 'RxSwift'
end
target 'Demo3' do
pod 'SwiftyJSON'
end
end
Abstract Target
上面例子中,因爲 Demo1 與 Demo2 都須要依賴 Alamofire,咱們經過 Target 嵌套讓 Demo2 來繼承 Demo1 的 Pods 庫依賴。
這麼作可能會有一個限制,就是當 Demo1 的 Pod 依賴並不是所有爲 Demo2 所須要的時候,就會有依賴冗餘。此時就須要 Abstract Target
登場了。例如:
abstract_target 'Networking' do
pod 'Alamofire'
target 'Demo1' do
pod 'RxSwift'
end
target 'Demo2' do
pod 'ReactCocoa'
end
target 'Demo3' do
end
end
將網絡請求的 Pod 依賴抽象到 Networking
target 中,這樣就能避免 Demo2 對 RxSwift 的依賴。
這種方式配置所生成的 Pods.project
並不會存在名稱爲 Networking
的 Target,它僅會在主工程的專屬 Target 中留下印記:
總結
本文結合 Xcode 工程結構來展開 CocoaPods-Core 的 Podfile 之旅,主要感覺以下:
-
再一次感覺了 Ruby 語言的動態之美,給我一個字符串,還你一個未知世界; -
結合 Xcode 工程結構更好的理解了 Podfile 的設計初衷, 基礎知識很重要; -
所謂「算法無用論」這種事情,在計算機的世界是不存在的,沒有好的數據結構知識如何更好的抽象; -
瞭解 Podfile 的 DSL 是如何映射到內存中,又是如何來存儲每一個關鍵數據的
知識點問題梳理
這裏羅列了四個問題用來考察你是否已經掌握了這篇文章,若是沒有建議你加入 收藏 再次閱讀:
-
說說 TargetDefinition
的數據結構 ? -
說說 TargetDefinition
與 Xcode Project 的關係 ? -
Podfile
的文件格式有幾種,分別是如何加載 ? -
Lockfile
和Podfile
的關係
參考資料
CocoaPods 命令解析: https://zhuanlan.zhihu.com/p/212101448
[2]Podspec
: https://guides.cocoapods.org/syntax/podspec.html
Build Phases: https://www.objc.io/issues/6-build-tools/build-process/#controlling-the-build-process
[4]Build Settings: https://developer.apple.com/library/archive/featuredarticles/XcodeConcepts/Concept-Build_Settings.html#//apple_ref/doc/uid/TP40009328-CH6-SW1
[5]Build Configurations: https://medium.com/practical-ios-development/some-practical-uses-for-xcode-build-schemes-and-build-configurations-swift-e50d15a1304f
[6]Multiple Xcodeproj Generation: http://blog.cocoapods.org/CocoaPods-1.7.0-beta/
[7]官方定義: https://yaml.org/
[8]initialize: https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/objinitialization.html
[9]CocoaPods 核心組件: https://zhuanlan.zhihu.com/p/187272448
[10]instance_eval: https://ruby-doc.org/core-2.7.0/BasicObject.html
[11]StackOverFlow: https://stackoverflow.com/questions/900419/how-to-understand-the-difference-between-class-eval-and-instance-eval
[12]yaml: https://github.com/ruby/yaml
[13]Podfile 手冊: https://guides.cocoapods.org/syntax/podfile.html#pod
本文分享自微信公衆號 - 一瓜技術(tech_gua)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。