鍍金池/ 教程/ iOS/ Build 過(guò)程
與四軸無(wú)人機(jī)的通訊
在沙盒中編寫(xiě)腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開(kāi)發(fā)
Collection View 動(dòng)畫(huà)
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫(huà)
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫(huà)解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫(huà)
常見(jiàn)的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無(wú)人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無(wú)止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫(huà)
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺(jué)
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開(kāi)發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語(yǔ)言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過(guò)程

Build 過(guò)程

近些日子我們被寵壞了 -- 我們只需要單擊 Xcode 中的一個(gè)按鈕(這個(gè)按鈕看起來(lái)有點(diǎn)像是在播放一些音樂(lè)的動(dòng)作),過(guò)幾秒鐘之后,我們的程序就會(huì)運(yùn)行起來(lái)了,除非遇到一些錯(cuò)誤,這非常的神奇。

在本文中,我們將從更高級(jí)別的角度來(lái)解讀 Build 過(guò)程,并探索一下在 Xcode 界面中暴露出的 project setting 信息與 Build 過(guò)程有什么關(guān)系。為了更加深入的探索 Build 過(guò)程中,每一步實(shí)際執(zhí)行的工作,我都會(huì)在本文中引入一些別的文章。

解密 Build 日志

為了了解 Xcode build 過(guò)程的內(nèi)部工作原理,我們首先把突破口瞄準(zhǔn)完整的 log 文件上。打開(kāi) Log Navigator ,從列表中選擇一個(gè) Build ,Xcode 會(huì)將 log 文件很完美的展現(xiàn)出來(lái)。

http://wiki.jikexueyuan.com/project/objc/images/6-1.png" alt="" />

默認(rèn)情況下,上面的 Xcode 界面中隱藏了大量的信息,我們通過(guò)選擇任務(wù),然后點(diǎn)擊右邊的展開(kāi)按鈕,就能看到每個(gè)任務(wù)的詳細(xì)信息。另外一種可選的方案就是選中列表中的一個(gè)或者多個(gè)任務(wù),然后選擇組合鍵 Cmd-C,這將會(huì)把所有的純文本信息拷貝至粘貼板。最后,我們還可以選擇 Editor 菜單中的 "Copy transcript for shown results",以此將所有的 log 信息拷貝到粘貼板中。

本文給出的示例中,log 信息將近有 10,000 行(其實(shí)大多數(shù)的 log 信息是編譯 OpenSSL 時(shí)生成的,并不是我們自己所寫(xiě)的代碼生成的)。下面我們就開(kāi)始吧!

注意觀察輸出的 log 信息,首先會(huì)發(fā)現(xiàn) log 信息被分為不同的幾大塊,它們與我們工程中的targets相互對(duì)應(yīng)著:

Build target Pods-SSZipArchive
...
Build target Makefile-openssl
...
Build target Pods-AFNetworking
...
Build target crypto
...
Build target Pods
...
Build target ssl
...
Build target objcio

本文涉及到的工程有幾個(gè)依賴項(xiàng):其中 AFNetworking 和 SSZipArchive 包含在 Pods 中,而 OpenSSL 則以子工程的形式包含在工程中。

針對(duì)工程中的每個(gè) target,Xcode 都會(huì)執(zhí)行一系列的操作,將相關(guān)的源碼,根據(jù)所選定的平臺(tái),轉(zhuǎn)換為機(jī)器可讀的二進(jìn)制文件。下面我們?cè)敿?xì)的了解一下第一個(gè) target:SSZipArchive。

在針對(duì)這個(gè) target 輸出的 log 信息中,我們可以看到每個(gè)任務(wù)被執(zhí)行的詳細(xì)情況。例如第一個(gè)任務(wù)是處理一個(gè)預(yù)編譯頭文件(為了增強(qiáng) log 信息的可讀性,我省略了許多細(xì)節(jié)):

(1) ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7 objective-c com.apple.compilers.llvm.clang.1_0.compiler
    (2) cd /.../Dev/objcio/Pods
        setenv LANG en_US.US-ASCII
        setenv PATH "..."
    (3) /.../Xcode.app/.../clang 
            (4) -x objective-c-header 
            (5) -arch armv7 
            ... configuration and warning flags ...
            (6) -DDEBUG=1 -DCOCOAPODS=1 
            ... include paths and more ...
            (7) -c 
            (8) /.../Pods-SSZipArchive-prefix.pch 
            (9) -o /.../Pods-SSZipArchive-prefix.pch.pch

在 build 處理過(guò)程中,每個(gè)任務(wù)都會(huì)出現(xiàn)類似上面的這些 log 信息,我們就通過(guò)上面的 log 信息進(jìn)一步了解詳情。

  1. 類似上面的每個(gè) log 信息塊都會(huì)利用一行 log 信息來(lái)描述相關(guān)的任務(wù)作為起點(diǎn)。
  2. 接著輸出帶縮進(jìn)的3行 log 信息,列出了該任務(wù)執(zhí)行的語(yǔ)句。此處,工作目錄發(fā)生了改變,并對(duì) LANG 和 PATH 環(huán)境變量進(jìn)行設(shè)置。
  3. 這里是發(fā)生奇跡的地方。為了處理一個(gè).pch文件,調(diào)用了 clang,并附帶了許多可選項(xiàng)。下面跟著輸出的 log 信息顯示了完整的調(diào)用過(guò)程,以及所有的參數(shù)。我們看看其中的幾個(gè)參數(shù)...
  4. -x 標(biāo)示符用來(lái)指定所使用的語(yǔ)言,此處是 objective-c-header。
  5. 目標(biāo)架構(gòu)指定為 armv7。
  6. 暗示 #defines 的內(nèi)容已經(jīng)被添加了。
  7. -c 標(biāo)示符用來(lái)告訴 clang 具體該如何做。-c 表示:運(yùn)行預(yù)處理器、詞法分析器、類型檢查、LLVM 的生成和優(yōu)化,以及 target 指定匯編代碼的生成階段,最后,運(yùn)行匯編器以產(chǎn)出一個(gè).o的目標(biāo)文件。
  8. 輸入文件。
  9. 輸出文件。

雖然有大量的 log 信息,不過(guò)我不會(huì)對(duì)每個(gè)任務(wù)做詳細(xì)的介紹。我們的重點(diǎn)是讓你全面的了解在整個(gè) build 過(guò)程中,哪些工具會(huì)被調(diào)用,以及背后會(huì)使用到了哪些參數(shù)。

針對(duì)這個(gè) target ,雖然只有一個(gè) .pch 文件,但實(shí)際上這里對(duì) objective-c-header 文件的處理有兩個(gè)任務(wù)。通過(guò)觀察具體輸出的 log 信息,我們可以知道詳情:

ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7 objective-c ...
ProcessPCH /.../Pods-SSZipArchive-prefix.pch.pch Pods-SSZipArchive-prefix.pch normal armv7s objective-c ...

從上面的 log 信息中,可以明顯的看出 target 針對(duì)兩種架構(gòu)做了 build -- armv7 和 armv7s -- 因此 clang 對(duì)文件做了兩次處理,每次針對(duì)一種架構(gòu)。

在處理預(yù)編譯頭文件之后,可以看到針對(duì) SSZipArchive target 有另外的幾個(gè)任務(wù)類型。

CompileC ...
Libtool ...
CreateUniversalBinary ...

顧名思義:CompileC 用來(lái)編譯 .m.c 文件,Libtool 用來(lái)從目標(biāo)文件中構(gòu)建 library,而 CreateUniversalBinary 則將上一階段產(chǎn)生的兩個(gè) .a 文件(每個(gè)文件對(duì)應(yīng)一種架構(gòu))合并為一個(gè)通用的二進(jìn)制文件,這樣就能同時(shí)在 armv7 和 armv7s 上面運(yùn)行。

接著,在工程中其它一些依賴項(xiàng)也會(huì)發(fā)生于此類似的步驟。AFNetworking 被編譯之后,會(huì)與 SSZipArchive 進(jìn)行鏈接,以當(dāng)做 pod library。OpenSSL 編譯之后,會(huì)接著處理 crypto 和 ssl target。

當(dāng)所有的依賴項(xiàng)都 build 完成之后,就輪到我們程序的 target 了。Build 該 target 時(shí),輸出的 log 信息會(huì)包含一些非常有價(jià)值,并且之前沒(méi)有出現(xiàn)過(guò)的內(nèi)容:

PhaseScriptExecution ...
DataModelVersionCompile ...
Ld ...
GenerateDSYMFile ...
CopyStringsFile ...
CpResource ...
CopyPNGFile ...
CompileAssetCatalog ...
ProcessInfoPlistFile ...
ProcessProductPackaging /.../some-hash.mobileprovision ...
ProcessProductPackaging objcio/objcio.entitlements ...
CodeSign ...

在上面的任務(wù)列表中,根據(jù)名稱不能區(qū)分的唯一任務(wù)可能就是 Ld,Ld 是一個(gè) linker 工具的名稱,與 libtool 非常相似。實(shí)際上,libtool也是簡(jiǎn)單的調(diào)用 ldlipo。'ld'被用來(lái)構(gòu)建可執(zhí)行文件,而libtool則用來(lái)構(gòu)建 library 文件。閱讀DanielChris兩篇文章,可以了解到更多關(guān)于編譯和鏈接的工作原理。

上面每一個(gè)步驟,實(shí)際上都會(huì)調(diào)用相關(guān)的命令行工具來(lái)做實(shí)際的工作,這跟之前我們看到的的 ProcessPCH 類似。至此,我將不會(huì)繼續(xù)介紹這些 log 信息了,我將帶領(lǐng)大家從另外一個(gè)不同的角度來(lái)繼續(xù)探索這些任務(wù):Xcode 是如何知道哪些任務(wù)需要被執(zhí)行?

Build過(guò)程的控制

當(dāng)你選擇 Xcode 5 中的一個(gè)工程時(shí),會(huì)在 project editor 頂部顯示出 6 個(gè) tabs:General, Capabilities, Info, Build Settings, Build Phases 以及 Build Rules。

http://wiki.jikexueyuan.com/project/objc/images/6-2.png" alt="" />

對(duì)于我們理解 build 過(guò)程來(lái)說(shuō),其中最后 3 項(xiàng)與 build 過(guò)程緊密相連。

Build Phases

Build Phases 代表著將代碼轉(zhuǎn)變?yōu)榭蓤?zhí)行文件的最高級(jí)別規(guī)則。里面描述了 build 過(guò)程中必須執(zhí)行的不同類型規(guī)則。

http://wiki.jikexueyuan.com/project/objc/images/6-3.png" alt="" />

首先是 target 依賴項(xiàng)的構(gòu)建。這里會(huì)告訴 build 系統(tǒng),build 當(dāng)前的 target 之前,必須先對(duì)這里的依賴性進(jìn)行 build。實(shí)際上這并不屬于真正的 build phase,在這里,Xcode 只不過(guò)將其與 build phase 顯示到一塊罷了。

接著在 build phase中是一個(gè) CocoaPods 相關(guān)的腳本 script execution,接著在 Compile Sources section 中規(guī)定了所有必須參與編譯的文件。需要留意的是,這里并沒(méi)有指明這些文件是如何被編譯處理的。關(guān)于處理這些文件的更多內(nèi)容,我們將在研究 build rules 和 build settings 時(shí)學(xué)習(xí)到。此處列出的所有文件將根據(jù)相關(guān)的 rules 和 settings 被處理。

當(dāng)編譯結(jié)束之后,接下來(lái)就是將編譯所生成的目標(biāo)文件鏈接到一塊。注意觀察,Xcode 中的 build phase 之后是:"Link Binary with Libraries." 這里面列出了所有的靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù),這些庫(kù)會(huì)參與上面編譯階段生成的目標(biāo)文件進(jìn)行鏈接。靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的處理過(guò)程有非常大的區(qū)別,相關(guān)內(nèi)容請(qǐng)參考 Daniel的文章 Mach-O 可執(zhí)行文件。

當(dāng)鏈接完成之后,build phase 中最后需要處理的就是將靜態(tài)資源(例如圖片和字體)拷貝到 app bundle 中。需要注意的是,如果圖片資源是PNG格式,那么不僅僅對(duì)其進(jìn)行拷貝,還會(huì)做一些優(yōu)化(如果 build settings 中的 PNG 優(yōu)化是打開(kāi)的話)。

雖然靜態(tài)資源的拷貝是 build phase 中的最后一步,但 build 還沒(méi)有完成。例如,還沒(méi)有進(jìn)行 code signing (這并不是 build phase 考慮的范疇),code signing 屬于 build 步驟中的最后一步 "Packaging"。

定制Build Phases

至此,如果不考慮默認(rèn)設(shè)置的話,你已經(jīng)可以完全掌握了上面介紹的 build phases。例如,你可以在 build phases 中添加運(yùn)行自定義腳本,就像CocoaPods使用的一樣,來(lái)做額外的工作。當(dāng)然也可以添加一些資源的拷貝任務(wù),當(dāng)你需要將某些確定的資源拷貝到指定的 target 目錄中,這非常有用。

另外定制 build phases 有一個(gè)非常好用的功能:添加帶有水?。òò姹咎?hào)和 commit hash)的 app icon -- 只需要在 build phase 中添加一個(gè) "Run Script",并用下面的命令來(lái)獲取版本號(hào)和 commit hash:

version=`/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "${INFOPLIST_FILE}"`
commit=`git rev-parse --short HEAD`

然后使用 ImageMagick 來(lái)修改 app icon。這里有一個(gè)完整的示例,可以參考。

如果你希望自己或者別人編寫(xiě)的代碼看起來(lái)比較簡(jiǎn)潔點(diǎn),可以添加一個(gè) "Run Script":如果一個(gè)源文件超過(guò)指定行數(shù),就發(fā)出警告。如下代碼所示,設(shè)置的行數(shù)為 200。

find "${SRCROOT}" \( -name "*.h" -or -name "*.m" \) -print0 | xargs -0 wc -l | awk '$1 > 200 && $2 != "total" { print $2 ":1: warning: file more than 200 lines" }'

Build Rules

Build rules 指定了不同的文件類型該如何編譯。一般來(lái)說(shuō),開(kāi)發(fā)者并不需要修改這里面的內(nèi)容。如果你需要對(duì)特定類型的文件添加處理方法,那么可以在此處添加一條新的規(guī)則。

一條 build rule 指定了其應(yīng)用于哪種類型文件,該類型文件是如何被處理的,以及輸出的內(nèi)容該如何處置。比方說(shuō),我們創(chuàng)建了一條預(yù)處理規(guī)則,該規(guī)則將 Objective-C 的實(shí)現(xiàn)文件當(dāng)做輸入,解析文件中的注釋內(nèi)容,最后再輸出一個(gè) .m 文件,文件中包含了生成的代碼。由于我們不能將 .m 文件既當(dāng)做輸入又當(dāng)做輸出,所以我使用了 .mal 后綴,定制的 build rule 如下所示:

http://wiki.jikexueyuan.com/project/objc/images/6-4.png" alt="" />

上面的規(guī)則應(yīng)用于所有后綴為 *.mal 的文件,這些文件會(huì)被自定義的腳本處理(調(diào)用我們的預(yù)處理器,并附帶上輸入和輸出參數(shù))。最后,該規(guī)則告訴 build system 在哪里可以找到此規(guī)則的輸出文件。

在腳本中,我使用了少量的變量來(lái)指定正確的路徑和文件名。在蘋(píng)果的 Build Setting Reference 文檔中可以找到所有可用的變量。build 過(guò)程中,要想觀察所有已存在的環(huán)境變量,你可以在 build phase 中添加一個(gè) "Run Script",并勾選上 "Show environment variables in build log"。

Build Settings

至此,我們已經(jīng)了解到在 build phases 中是如何定義 build 處理的過(guò)程,以及 build rules 是如何指定哪些文件類型在編譯階段需要被預(yù)處理。在 build settings 中,我們可以配置每個(gè)任務(wù)(之前在 build log 輸出中看到的任務(wù))的詳細(xì)內(nèi)容。

你會(huì)發(fā)現(xiàn) build 過(guò)程的每一個(gè)階段,都有許多選項(xiàng):從編譯、鏈接一直到 code signing 和 packaging。注意,settings 是如何被分割為不同的部分 -- 其實(shí)這大部分會(huì)與 build phases 有關(guān)聯(lián),有時(shí)候也會(huì)指定編譯的文件類型。

這些選項(xiàng)基本都有很好的文檔介紹,你可以在右邊面板中的 quick help inspector 或者 Build Setting Reference 中查看到。

工程文件

上面我們介紹的所有內(nèi)容都被保存在工程文件(.pbxproj)中,除了其它一些工程相關(guān)信息(例如 file groups),我們很少會(huì)深入該文件內(nèi)部,除非在代碼 merge 時(shí)發(fā)生沖突,或許會(huì)進(jìn)去看看。

建議你用文本編輯器打開(kāi)一個(gè)工程文件,從頭到尾看一遍里面的內(nèi)容。它的可讀性非常高,里面的許多內(nèi)容一看就知道什么意思了,不會(huì)存在太大的問(wèn)題。通過(guò)閱讀并完全理解工程文件,這對(duì)于合并工程文件的沖突非常有幫助。

首先,我們來(lái)看看文件中叫做 rootObject 的條目。在我的工程中,如下所示:

rootObject = 1793817C17A9421F0078255E /* Project object */;

根據(jù)這個(gè) ID(1793817C17A9421F0078255E),我們可以找到 main 工程的定義:

/* Begin PBXProject section */
    1793817C17A9421F0078255E /* Project object */ = {
        isa = PBXProject;
...

在這部分中有一些 keys,順從這些 key,我們可以了解到更多關(guān)于這個(gè)工程文件的組成。例如,mainGroup 指向了 root file group。如果你按照這個(gè)思路,你可以快速了解到在 .pbxproj 文件中工程的結(jié)構(gòu)。下面我要來(lái)介紹一些與 build 過(guò)程相關(guān)的內(nèi)容。其中 target key 指向了 build target 的定義:

targets = (
    1793818317A9421F0078255E /* objcio */,
    170E83CE17ABF256006E716E /* objcio Tests */,
);

根據(jù)第一個(gè)內(nèi)容,我們找到一個(gè) target 的定義:

1793818317A9421F0078255E /* objcio */ = {
    isa = PBXNativeTarget;
    buildConfigurationList = 179381B617A9421F0078255E /* Build configuration list for PBXNativeTarget "objcio" */;
    buildPhases = (
        F3EB8576A1C24900A8F9CBB6 /* Check Pods Manifest.lock */,
        1793818017A9421F0078255E /* Sources */,
        1793818117A9421F0078255E /* Frameworks */,
        1793818217A9421F0078255E /* Resources */,
        FF25BB7F4B7D4F87AC7A4265 /* Copy Pods Resources */,
    );
    buildRules = (
    );
    dependencies = (
        1769BED917CA8239008B6F5D /* PBXTargetDependency */,
        1769BED717CA8236008B6F5D /* PBXTargetDependency */,
    );
    name = objcio;
    productName = objcio;
    productReference = 1793818417A9421F0078255E /* objcio.app */;
    productType = "com.apple.product-type.application";
};

其中 buildConfigurationList 指向了可用的配置項(xiàng),一般是 DebugRelease。根據(jù) debug 對(duì)應(yīng)的 id,我們可以找到 build setting tab 中所有選項(xiàng)存儲(chǔ)的位置:

179381B717A9421F0078255E /* Debug */ = {
    isa = XCBuildConfiguration;
    baseConfigurationReference = 05D234D6F5E146E9937E8997 /* Pods.xcconfig */;
    buildSettings = {
        ALWAYS_SEARCH_USER_PATHS = YES;
        ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
        CODE_SIGN_ENTITLEMENTS = objcio/objcio.entitlements;
...

buildPhases 屬性則簡(jiǎn)單的列出了在 Xcode 中定義的所有 build phases。這非常容易識(shí)別出來(lái)(Xcode 中的參數(shù)使用了它們?cè)菊嬲拿?,并?C 風(fēng)格進(jìn)行注釋)。buildRules 屬性是空的:因?yàn)樵谠摴こ讨?,我沒(méi)有自定義 build rules。dependencies 列出了在 Xcode build phase tab 中列出的 target 依賴項(xiàng)。

沒(méi)那么嚇人,不是嗎?工程中剩下的內(nèi)容就留給你去當(dāng)做練習(xí)來(lái)了解吧。只需要順著對(duì)象的 ID 走,即可,一旦你找到了敲門(mén),理解了Xcode中工程設(shè)置的不同 section ,那么對(duì)于 merge 工程文件的沖突時(shí),將變得非常簡(jiǎn)單。甚至可以在 GitHub 中就能閱讀工程文件,而不用將工程文件 clone 到本地,并用 Xcode 打開(kāi)。

小結(jié)

當(dāng)今的軟件是都用其它復(fù)雜的一些軟件和資源開(kāi)發(fā)出來(lái)的,例如 library 和 build 工具等。反過(guò)來(lái),這些工具是構(gòu)建于底層架構(gòu)的,這猶如剝洋蔥一樣,一層包著一層。雖然這樣一層一層的,給人感覺(jué)太復(fù)雜,但是你完全可以去深入了解它們,這非常有助于你對(duì)軟件的深入理解,實(shí)際上當(dāng)你了解之后,這并沒(méi)有想象中的那么神奇,只不過(guò)它是一層一層堆砌起來(lái)的,每一層都是基于下一層構(gòu)建起來(lái)的。

本文所探索 build system 的內(nèi)部機(jī)制猶如剝掉洋蔥的一層。其實(shí)當(dāng)我們點(diǎn)擊 Xcode 中的運(yùn)行按鈕時(shí),我們并沒(méi)必要理解這個(gè)動(dòng)作涉及到的所有內(nèi)容。我們只是深入理解某一層,然后找到一個(gè)有組織的、并且可控的調(diào)用其它工具的順序,如果我們?cè)敢獾脑?,可以做進(jìn)一步的探索。我建議你閱讀本期中的其它文章,以進(jìn)一步了解這個(gè)洋蔥的下一層內(nèi)容!