1. 程式人生 > >通過Xcodeproj深入探究Xcode工程檔案

通過Xcodeproj深入探究Xcode工程檔案

你是否好奇Cocoapods是如何修改掉Xcode工程的結構?你也是否曾被Xcode工程的配置檔案裡面雜亂的內容搞得摸不清頭腦?你又是否知道Xcodeproj這個神奇的Ruby庫?下面我將通過這個系列來解除你的困惑。

Cocoapods是如何修改Xcode工程結構的?

我們知道Cocoapods是用ruby創作的一套第三方庫,它很方便的可以刪除、新增、更新第三方庫?當你執行修改完PodFile執行pod update的時候,你會驚訝的發現Xcode工程被神奇的修改掉了。那麼它是如何做到的呢?細心的你會發現,每個Xcode工程都有一個project.pbxproj檔案,這個檔案記錄著該工程的檔案結構。Cocoapods正是通過它的元件

Xcodeproj來對工程結構進行修改。

project.pbxproj這個檔案裡面的內容到底是什麼含義?

如果你使用過SVN或者Git進行團隊協作開發肯定會不可避免的遇到在合併程式碼的時候往往由於有過新增和刪除檔案的操作導致Xcode工程報錯打不開,這時候一般的解決思路是開啟project.pbxproj檔案,Command+F鍵入======或者<<<<<來找到衝突的地方,將衝突的內容刪除。然而有些人並不知道為何要這樣解決甚至不知道里面的內容是何意思?下面的內容或許對你有些許幫助。

project.pbxproj介紹

project.pbxproj採用的是老式風格的plist檔案(old ASCII plist),這最早是Next公司採用的一種檔案格式,它跟XML

格式很多地方類似,但是又有些許的不同。為了更方便理解,我建議你新建一個工程或者在以後的工程上開啟project.pbxproj,在例項的基礎上便於直觀感受,更有助於 加深理解。

首先我要介紹它裡面的眾多元素,例如

objc 根節點 PBXBuildFile PBXBuildPhase PBXAppleScriptBuildPhase PBXCopyFilesBuildPhase PBXFrameworksBuildPhase PBXHeadersBuildPhase PBXResourcesBuildPhase PBXShellScriptBuildPhase PBXSourcesBuildPhase PBXContainerItemProxy PBXFileElement PBXFileReference PBXGroup PBXVariantGroup PBXTarget PBXAggregateTarget PBXLegacyTarget PBXNativeTarget PBXProject PBXTargetDependency XCBuildConfiguration XCConfigurationList

萬物皆物件的概念下,你尚可將他們理解為一個個,它們裡面的各個子元素就是一個個物件。最外層的每個元素如PBXBuildFile被稱為一個個Section,為方便理解,文章後面的內容我都將這些元素稱為類,將元素的例項成為物件。

project.pbxproj的整體結構(根節點)

``` // !$UTF8$! {    archiveVersion = 1;    classes = { };    objectVersion = 45;    objects = {...};    rootObject = 0867D690FE84028FC02AAC07 /* Project object */; }

```

如果你已經打開了一個project.pbxproj,你就會很容易看到這種結構,只不過objects裡面的各種類屬於第二層結構,rootObject位於檔案的最後一行。

唯一標識碼

細心的你會看到,上面的根節點裡面的rootObject後面是一串24位的16進位制數,它就是每個物件的唯一標識碼,它可以唯一標識檔案的每個物件,也就是說 每個元素的標識碼都是不同的。Xcode生成唯一標識碼的演算法可能引入了日期、序列和其它一些預定義的值,但是並沒有確切的文件說明具體的生成過程。值得注意的是,該唯一標識碼不僅在所在的工程中唯一,而且還是跨工程唯一。

PBXBuildFile

PBXBuildFile是檔案類,被PBXBuildPhase等作為檔案包含或被引用的資源。此時我已經新建了一個名為Xcode工程Demo的工程,此時的工程結構是這樣,如圖1所示。而此時的project.pbxprojPBXBuildFile的結構如圖2所示。

圖1

圖2

可以清楚的看到每個PBXBuildFile物件都是由以下的結構組成

objc 4D05CA6B1193055000125045 /* xxx.c in Sources */ = {    isa = PBXBuildFile;    fileRef = 4D05CA411193055000125045 /* xxx.c */; };

圖3

其中isa跟Objc中的物件的isa指標一樣,指向的是它的類,而fileRef則指向的是一個PBXFileReference物件,這個類將在下面介紹。 細心的你又會發現,為什麼圖1和圖2中的檔案個數不一致,卻和圖3中編譯時的檔案和資源統一。前者的差異是由於PBXFileReference所致,通過後者我們可以大膽猜測,PBXBuildFile中的物件是編譯時候需要確認的檔案和資源的集合,如果不信的話可以拖幾張圖片資源扔進工程中 ,經過驗證結果和預測的情況一致。

PBXFileReference

PBXFileReference用於跟蹤專案引用的每一個外部檔案,比如原始碼檔案、資原始檔、庫檔案、生成目標檔案等。具體表現如圖4。

圖4

它的結構如下:

objc 87293F901153D870007AFD45 /* objc.mm */ = {    isa = PBXFileReference;    fileEncoding = 4;    lastKnownFileType = sourcecode.cpp.objcpp;    name = monobjc.mm;    path = sources/monobjc.mm;    sourceTree = ""; };

裡面的每個key的含義,對照著實際工程,大家不妨自行揣測。 我們再將PBXBuildFilePBXFileReference放一起進行對比,如圖5。

圖5

AppDelegate.swift物件通過fileRef指向識別符號為F3E1481A1DA50A180059397CPBXFileReference物件,通過這個引用,一個PBXBuildFile物件就可以查到自己的具體資訊,如fileTypenamepath等資訊。

PBXGroup

PBXGroup用於組檔案,或者巢狀組。讓我們來看下例項,如圖6

圖6

怡然是通過唯一識別符號組裝,每個PBXGroup物件都有一個children屬性,裡面可以是任何一種類的物件。但是這時候的PBXGroup指的是Xcode裡面組織的分組結構,和實際檔案系統中的結構並不相同。 指的注意的是,children中的每個檔案物件都屬於PBXFileReference類,而不是PBXBuildFile

PBXNativeTarget

PBXNativeTarget就是工程中的target,如果工程中有多個target,都會在這個section中有所體現。 例項中如圖7所示

圖7

我們都知道每個target都有Compile SourcesCopy Bundle ResourcesLink Binary With Libiaries這三個需要在編譯時確定的內容。 而在PBXNativeTarget中通過buildPhases屬性可以找到對應的內容。

PBXSourcesBuildPhase和PBXResourcesBuildPhase

PBXSourcesBuildPhase用於構建階段中編譯原始檔,PBXResourcesBuildPhase用於構建階段需要複製的資原始檔,如圖8

圖8

需要注意的是,PBXSourcesBuildPhase這個section中放著所有的target的同類物件,PBXResourcesBuildPhase也是一樣。

PBXProject

PBXProject標識著整個工程,由根元素的rootObject引入。如圖9所示

圖9

該物件記錄著targetsmainGroup等重要資訊,甚至每個target在建立時候的Xcode版本都會記錄在其中。

其他元素

還有其他很多重要的元素,如記錄工程配置資訊的XCConfigurationListXCBuildConfiguration等,大家可以自行研究研究。

總結

由此看來,以前看到就頭疼的project.pbxproj配置檔案的內容並沒有想象中的複雜,也可以看出Xcode檔案組織的嚴密和周整。

大家自己研究的時候,不妨可以動手改改專案中的內容,再去觀察配置檔案的變化,這樣既可以有更深的理解,或許有新發現也說不定奧。

下篇文章,我將帶大家用Xcodeproj這個庫來,通過幾行程式碼修改project.pbxproj中的內容以達到通過指令碼去修改Xcode工程和分析工程的目的。

相關連結