全面認識Xcode中的project.pbxproj文件和簡單運用

本文為學習記錄所作,內容來源于玉令天下的博客-Let's Talk About project.pbxproj

概述

在此之前對于project.pbxproj這個文件只停留在git出文件沖突時, 使用Xcode打開它, 處理一下沖突并保存完事. 后來做項目混淆時, 其中一步是修改文件名, 而修改文件名之后原本Xcode中的引用就變成大紅色了. 而之前的做法是, 刪除引用并重新把改名后的文件拖進來, 因為文件多, Xcode需要加載好長時間. 為了解決這個問題, 故學習和使用到這個文件.

文件路徑

項目名.xcodeproj-> 右擊顯示包內容 -> project.pbxproj

文件格式:

它本質上是一種舊風格的plist文件, 與如今Xcode中使用plist文件格式不同(現(xiàn)在新的plist文件采用xml格式), 它這樣表示一個數(shù)組:

( "1", "2", "3" )

它這樣表示一個字典:

{
   "key" = "value";
   ...
}

內容規(guī)則

project.pbxproj使用 UUID 作為交叉引用的索引, 保證每個配置信息對象的唯一性. 因為 UUID 猜測是根據(jù)機器硬件和時間戳等要素生成, 避免了多人在同一時間段操作修改工程文件帶來的問題. 也就是說工程中每項配置對象(文件,目錄,配置表等等)都有個唯一的 UUID, 然后其他配置對象想引用某個配置對象直接使用它的 UUID 即可. 這就跟我們編程時使用指針指向某個對象的地址一樣, 其他對象的屬性想引用它, 只需要給屬性傳個指針地址就行了.

整體內容格式

// !$*UTF8*$!
{
    archiveVersion = 1;
    classes = {
    };
    objectVersion = 46;
    objects = {
        ....
        keys = values;(to much to show here)
        ....
    };
    rootObject = C5A75D711F5E7D2E000191E4 /* Project object */;
}

其中重要的 objectsrootObject, 因為其它幾個基本固定不變的. 所有的配置對象都放在objects對應的 value 中, 包括根對象rootObject. objects 對應的 value 也是一個字典, key 都為 UUID, value 依然是個字典. 讀懂 project.pbxproj 的最好方式就是順著 rootObject 的各個屬性對應的 UUID 在 objects 中找到對應的對象, 然后一層層看下去. 這樣整個文件的配置信息存放方式就慢慢摸清了.

配置信息對象的類型

objects 的鍵值對根據(jù)內容類型被分成了若干個section, section 的數(shù)量和種類跟工程有關, 根據(jù)工程的配置多少而不同, 下面列出了一個section 列表(非完整):

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

而每個section

 /* Begin xxxx section */  (以這種注釋開始) 
 ...
 /* End xxxx section */    (以這種注釋結束) 

一個section里面的內容類型是相同的. 而類型可以從isa這個key對應的value看出來. 比如項目中所有文件的引用是一種類型,
叫做 PBXFileReference. 我新建了一個項目, 隨意拖入的一些文件.

文件引用.png

而在objects中的表現(xiàn)如下:

/* Begin PBXFileReference section */
        C55BB9141F67BE1C00D17310 /* TwoPanScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TwoPanScrollView.h; sourceTree = "<group>"; };
        C55BB9151F67BE1C00D17310 /* TwoPanScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TwoPanScrollView.m; sourceTree = "<group>"; };
        C55FA2A220FDE9B7006BBB41 /* Person.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Person.h; sourceTree = "<group>"; };
        C55FA2A320FDE9B7006BBB41 /* Person.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Person.m; sourceTree = "<group>"; };
        C5616BC21FA858C000A9C033 /* UIView+Toast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Toast.h"; sourceTree = "<group>"; };
        C5616BC31FA858C000A9C033 /* UIView+Toast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Toast.m"; sourceTree = "<group>"; };
        C57DC7251F67FAF600106DF4 /* SecondViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecondViewController.h; sourceTree = "<group>"; };
        C57DC7261F67FAF600106DF4 /* SecondViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecondViewController.m; sourceTree = "<group>"; };
        C5A75D791F5E7D2E000191E4 /* 學習之路.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "學習之路.app"; sourceTree = BUILT_PRODUCTS_DIR; };
        C5A75D7D1F5E7D2E000191E4 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
        C5A75D7F1F5E7D2E000191E4 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
        C5A75D801F5E7D2E000191E4 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
        C5A75D821F5E7D2E000191E4 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
        C5A75D831F5E7D2E000191E4 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
        C5A75D861F5E7D2E000191E4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
        C5A75D881F5E7D2E000191E4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
        C5A75D8B1F5E7D2E000191E4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
        C5A75D8D1F5E7D2E000191E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
        C5A75D921F5E7D2E000191E4 /* 學習之路Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "學習之路Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
        C5A75D961F5E7D2E000191E4 /* ____Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "____Tests.m"; sourceTree = "<group>"; };
        C5A75D981F5E7D2E000191E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
        C5A75D9D1F5E7D2E000191E4 /* 學習之路UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "學習之路UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
        C5A75DA11F5E7D2E000191E4 /* ____UITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "____UITests.m"; sourceTree = "<group>"; };
        C5A75DA31F5E7D2E000191E4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */

再比如項目中所有文件的分組是一種類型, 叫做 PBXGroup. 如下:

/* Begin PBXGroup section */
        C5616BC11FA858C000A9C033 /* Toast */ = {
            isa = PBXGroup;
            children = (
                C5616BC21FA858C000A9C033 /* UIView+Toast.h */,
                C5616BC31FA858C000A9C033 /* UIView+Toast.m */,
            );
            path = Toast;
            sourceTree = "<group>";
        };
        C57EAA11212D02A4003D38CD /* TwoPanScrollView */ = {
            isa = PBXGroup;
            children = (
                C55BB9141F67BE1C00D17310 /* TwoPanScrollView.h */,
                C55BB9151F67BE1C00D17310 /* TwoPanScrollView.m */,
            );
            path = TwoPanScrollView;
            sourceTree = "<group>";
        };
        C57EAA12212D02D7003D38CD /* Class */ = {
            isa = PBXGroup;
            children = (
                C5A75D7F1F5E7D2E000191E4 /* AppDelegate.h */,
                C5A75D801F5E7D2E000191E4 /* AppDelegate.m */,
                C5A75D821F5E7D2E000191E4 /* ViewController.h */,
                C5A75D831F5E7D2E000191E4 /* ViewController.m */,
                C57DC7251F67FAF600106DF4 /* SecondViewController.h */,
                C57DC7261F67FAF600106DF4 /* SecondViewController.m */,
                C55FA2A220FDE9B7006BBB41 /* Person.h */,
                C55FA2A320FDE9B7006BBB41 /* Person.m */,
                C5616BC11FA858C000A9C033 /* Toast */,
                C57EAA11212D02A4003D38CD /* TwoPanScrollView */,
            );
            path = Class;
            sourceTree = "<group>";
        };
        C5A75D701F5E7D2E000191E4 = {
            isa = PBXGroup;
            children = (
                C5A75D7B1F5E7D2E000191E4 /* 學習之路 */,
                C5A75D951F5E7D2E000191E4 /* 學習之路Tests */,
                C5A75DA01F5E7D2E000191E4 /* 學習之路UITests */,
                C5A75D7A1F5E7D2E000191E4 /* Products */,
            );
            sourceTree = "<group>";
        };
        C5A75D7A1F5E7D2E000191E4 /* Products */ = {
            isa = PBXGroup;
            children = (
                C5A75D791F5E7D2E000191E4 /* 學習之路.app */,
                C5A75D921F5E7D2E000191E4 /* 學習之路Tests.xctest */,
                C5A75D9D1F5E7D2E000191E4 /* 學習之路UITests.xctest */,
            );
            name = Products;
            sourceTree = "<group>";
        };
        C5A75D7B1F5E7D2E000191E4 /* 學習之路 */ = {
            isa = PBXGroup;
            children = (
                C57EAA12212D02D7003D38CD /* Class */,
                C5A75D851F5E7D2E000191E4 /* Main.storyboard */,
                C5A75D881F5E7D2E000191E4 /* Assets.xcassets */,
                C5A75D8A1F5E7D2E000191E4 /* LaunchScreen.storyboard */,
                C5A75D8D1F5E7D2E000191E4 /* Info.plist */,
                C5A75D7C1F5E7D2E000191E4 /* Supporting Files */,
            );
            path = "學習之路";
            sourceTree = "<group>";
        };
        C5A75D7C1F5E7D2E000191E4 /* Supporting Files */ = {
            isa = PBXGroup;
            children = (
                C5A75D7D1F5E7D2E000191E4 /* main.m */,
            );
            name = "Supporting Files";
            sourceTree = "<group>";
        };
        C5A75D951F5E7D2E000191E4 /* 學習之路Tests */ = {
            isa = PBXGroup;
            children = (
                C5A75D961F5E7D2E000191E4 /* ____Tests.m */,
                C5A75D981F5E7D2E000191E4 /* Info.plist */,
            );
            path = "學習之路Tests";
            sourceTree = "<group>";
        };
        C5A75DA01F5E7D2E000191E4 /* 學習之路UITests */ = {
            isa = PBXGroup;
            children = (
                C5A75DA11F5E7D2E000191E4 /* ____UITests.m */,
                C5A75DA31F5E7D2E000191E4 /* Info.plist */,
            );
            path = "學習之路UITests";
            sourceTree = "<group>";
        };
/* End PBXGroup section */

更多配置對象屬性和類型以及含義可以參照這篇文章提供的對照表:Xcode Project File Format英文 翻譯版:中文翻譯

運用

  • 我們在使用 Cocoapods 時發(fā)現(xiàn)它可以更改Xcode工程目錄和一些配置信息, 這其實就是 Cocoapods 通過它的組件Xcodeproj 來對工程結構進行修改, 其中就包括讀取 project.pbxproj 信息, 進行相關的修改后再存入. 完成自動化導入第三方庫.
  • 一些自動化打包工具也會通過讀寫project.pbxproj 中的信息完成對證書的動態(tài)修改.
  • 簡單實踐一個例子. 比如我們將 Person.hPerson.m show in finder, 將他們的文件名稱修改為 FTPerson.hFTPerson.m. 回到Xcode會發(fā)現(xiàn)原來的引用已經(jīng)無效了.
    Person無效引用.png

    這時候我們可以直接打開 project.pbxproj , 全局搜索 Person 并替換為 FTPerson, 保存后發(fā)現(xiàn)Xcode中的引用恢復了正常. 這個也正是我在編寫腳本實現(xiàn)項目中所有文件改名后, 再通過修改 project.pbxproj 中的引用信息從而跳過了刪除引用再添加引用的這個耗時操作的原理.

參考

http://www.cocoachina.com/ios/20170110/18549.html

http://yulingtianxia.com/blog/2016/09/28/Let-s-Talk-About-project-pbxproj/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容