4. Podfile 的解析逻辑
本文作者:Edmond??
校對:冬瓜
CocoaPods 歷險記?這個專題是?Edmond?和?冬瓜?共同撰寫,對于 iOS / macOS 工程中版本管理工具 CocoaPods 的實現細節、原理、源碼、實踐與經驗的分享記錄,旨在幫助大家能夠更加了解這個依賴管理工具,而不僅局限于?pod install?和?pod update。
本文知識目錄
引子
在上文 「CocoaPods 命令解析?- CLAide」?中,我們通過對 CLAide 的源碼分析,了解了 CocoaPods 是如何處理 pod 命令,多級命令又是如何組織和嵌套的,并解釋了命令行輸出所代表的含義。今天我們開始學習 Podfile 。
大多 iOS 工程師最先接觸到的 CocoaPods 概念應該是 Podfile,而 Podfile 屬于 cocoapods-core(以下簡稱 Core) 的兩大概念之一。另外一個則是 Podspec[2] (用于描述 Pod Library 的配置文件),只有當你需要開發 Pod 組件的時候才會接觸。
在介紹 Podfile 的內容結構之前,必須要談談 Xcode 的工程結構。
Xcode 工程結構
我們先來看一個極簡 Podfile 聲明:
target?'Demo'?dopod?'Alamofire',?:path?=>?'./Alamofire' end它編譯后的工程目錄如下:
如你所見 Podfile 的配置是圍繞 Xcode 的這些工程結構:Workspace、Project、Target 以及?Build Setting 來展開的。作為包管理工具 CocoaPods 將所管理的 Pods 依賴庫組裝成一個個 Target,統一放入 Pods project 中的 Demo target,并自動配置好 Target 間的依賴關系。
之后將 Example 主工程和 Pods 工程一起打包到新建的 Example.workspace,配好主工程與 Pods 工程之間的依賴,完成最終轉換。
接下來,我們來聊一聊這些 Xcode 結構:
Target - 最小可編譯單元
首先是 Target,它作為工程中最小的可編譯單元,根據 Build Phases[3] 和 Build Settings[4] 將源碼作為輸入,經編譯后輸出結果產物。
其輸出結果可以是鏈接庫、可執行文件或者資源包等,具體細節如下:
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?Podrequire?'cocoapods-core/gem_version'class?PlainInformative?<?StandardError;?endclass?Informative?<?PlainInformative;?endrequire?'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?文件的?classautoload?: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?文件的?classautoload?: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?Podclass?Podfileclass?TargetDefinition#?父節點:?TargetDefinition?或者?Podfileattr_reader?:parent#?子節點:?TargetDefinitionattr_reader?:children#?記錄?tareget?的配置信息attr_accessor?:internal_hashdef?root?parent.is_a?(Podfile)?||?parent.nil?enddef?rootif?root?selfelseparent.rootendenddef?podfileroot.parentend#?...endend 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?Podclass?Podfileinclude?Pod::Podfile::DSL#?podfile?路徑attr_accessor?:defined_in_file#?所有的?TargetDefinition?的根節點,?正常只有一個,即?Pods.project?targetattr_accessor?:root_target_definitions#?記錄?Pods.project?項目的配置信息attr_accessor?:internal_hash#?當前?DSL?解析使用的?TargetDefinitionattr_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:?0dda98a0ed7eec4bdcd5fe3cdd35fcd2b3022825PODFILE?CHECKSUM:?da12cc12a30cfb48ebc5d14e8f51737ab65e8241COCOAPODS:?1.10.0.beta.2我們來分析一下,通過該 Lockfile 能夠獲取哪些信息:
| 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', ].freezeCocoaPods 按照上述命名優先級來查找工程目錄下所對應的 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?Podclass?Podfileinclude?Pod::Podfile::DSLdef?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.extnamewhen?'',?'.podfile',?'.rb'Podfile.from_ruby(path)when?'.yaml'#?現在也支持了?.yaml?格式Podfile.from_yaml(path)elseraise?Informative,?"Unsupported?Podfile?format?`#{path}`."endend endfrom_file 在 pod install 命令執行后的 verify_podfile_exists! 中被調用的:
def?verify_podfile_exists!unless?config.podfileraise?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?+?filenameif?candidate.file?return?candidateendendnil enddef?podfile_path@podfile_path?||=?podfile_path_in_dir(installation_root) enddef?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)?dobegineval(contents,?nil,?path.to_s)rescue?Exception?=>?emessage?=?"Invalid?`#{path.basename}`?file:?#{e.message}"raise?DSLError.new(message,?path,?e,?contents)endendpodfile 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_hashif?blockdefault_target_def?=?TargetDefinition.new('Pods',?self)default_target_def.abstract?=?true@root_target_definitions?=?[default_target_def]@current_target_definition?=?default_target_definstance_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,參數 2 internal_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/1539if?string.respond_to?(:encoding)?&&?string.encoding.name?!=?'UTF-8'string.encode!('UTF-8')endhash?=?YAMLHelper.load_string(string)from_hash(hash,?path) enddef?self.from_hash(hash,?path?=?nil)internal_hash?=?hash.duptarget_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?<<?definitionendpodfile 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'?dopod?'Alamofire' end當解析到 pod 'Alamofire' 時,會先通過 eval(contents, nil, path.to_s 將其轉換為 dsl.rb 中的方法:
def?pod(name?=?nil,?*requirements)unless?nameraise?StandardError,?'A?dependency?requires?a?name.'endcurrent_target_definition.store_pod(name,?*requirements) endname 為 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?firstparse_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?}elsepod?=?nameendget_hash_value('dependencies',?[])?<<?podnil enddef?get_hash_value(key,?base_value?=?nil)unless?HASH_KEYS.include?(key)raise?StandardError,?"Unsupported?hash?key?`#{key}`"endinternal_hash[key]?=?base_value?if?internal_hash[key].nil?internal_hash[key] enddef?set_hash_value(key,?value)unless?HASH_KEYS.include?(key)raise?StandardError,?"Unsupported?hash?key?`#{key}`"endinternal_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(nameplatformpodspecsexclusivelink_withlink_with_first_targetinhibit_warningsuse_modular_headersuser_project_pathbuild_configurationsproject_namesdependenciesscript_phaseschildrenconfiguration_pod_whitelistuses_frameworksswift_version_requirementsinheritanceabstractswift_version ).freeze整個映射過程如下:
精細化的 Podfile 配置
最后一節讓我們來 Show 一下 ,看看 Podfile 所謂的 targets 之間的依賴關系可以玩出什么花來 ???? 。
Target 嵌套
最簡單的 Podfile 就是文章開頭所展示的,不過在 Podfile 中還可以對 Target 進行嵌套使用。
假設在我們的主工程同時維護了三個項目,它們都依賴了 Alamofire,通過俄羅斯套娃就能輕松滿足條件:
target?'Demo1'?dopod?'Alamofire'target?'Demo2'?dotarget?'Demo3'?doendend end編譯后的 Pods.project 項目結構如下:
我們知道,CocoaPods 在 Pods.project 中為每個在 Podfile 中聲明的 Target 生成一個與之對應的專屬 Target 來集成它的 Pod 依賴。
對于有依賴關系的 Target 其生成的專屬 Target 名稱則會按照依賴關系疊加來命名,如 ?target Demo3 的專屬 Target 名稱為 Pods-Demo1-Demo2-Demo3。安裝完成后主項目將會引入該專屬 Target 來完成依賴關聯,如 Demo3:
關于 Target 嵌套,一個父節點是可以有多個子節點的:
target?'Demo1'?dopod?'Alamofire'target?'Demo2'?dopod?'RxSwift'endtarget?'Demo3'?dopod?'SwiftyJSON'end endAbstract Target
上面例子中,由于 Demo1 與 Demo2 都需要依賴 Alamofire,我們通過 Target 嵌套讓 Demo2 來繼承 Demo1 的 Pods 庫依賴。
這么做可能會有一個限制,就是當 Demo1 的 Pod 依賴并非全部為?Demo2 所需要的時候,就會有依賴冗余。此時就需要 Abstract Target 登場了。例如:
abstract_target?'Networking'?dopod?'Alamofire'target?'Demo1'?dopod?'RxSwift'endtarget?'Demo2'?dopod?'ReactCocoa'endtarget?'Demo3'?doend 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 的關系
參考資料
[1]
CocoaPods 命令解析: https://zhuanlan.zhihu.com/p/212101448
[2]Podspec: https://guides.cocoapods.org/syntax/podspec.html
[3]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
總結
以上是生活随笔為你收集整理的4. Podfile 的解析逻辑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LDA算法原理及matlab实现
- 下一篇: 打印ASCII码