全面認(rèn)識(shí)Xcode中的project.pbxproj文件和簡單運(yùn)用

本文為學(xué)習(xí)記錄所作,內(nèi)容來源于玉令天下的博客-Let's Talk About project.pbxproj

概述

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

文件路徑

項(xiàng)目名.xcodeproj-> 右擊顯示包內(nèi)容 -> project.pbxproj

文件格式:

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

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

它這樣表示一個(gè)字典:

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

內(nèi)容規(guī)則

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

整體內(nèi)容格式

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

其中重要的 objectsrootObject, 因?yàn)槠渌鼛讉€(gè)基本固定不變的. 所有的配置對(duì)象都放在objects對(duì)應(yīng)的 value 中, 包括根對(duì)象rootObject. objects 對(duì)應(yīng)的 value 也是一個(gè)字典, key 都為 UUID, value 依然是個(gè)字典. 讀懂 project.pbxproj 的最好方式就是順著 rootObject 的各個(gè)屬性對(duì)應(yīng)的 UUID 在 objects 中找到對(duì)應(yīng)的對(duì)象, 然后一層層看下去. 這樣整個(gè)文件的配置信息存放方式就慢慢摸清了.

配置信息對(duì)象的類型

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

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

而每個(gè)section

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

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

文件引用.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 /* 學(xué)習(xí)之路.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "學(xué)習(xí)之路.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 /* 學(xué)習(xí)之路Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "學(xué)習(xí)之路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 /* 學(xué)習(xí)之路UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "學(xué)習(xí)之路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 */

再比如項(xiàng)目中所有文件的分組是一種類型, 叫做 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 /* 學(xué)習(xí)之路 */,
                C5A75D951F5E7D2E000191E4 /* 學(xué)習(xí)之路Tests */,
                C5A75DA01F5E7D2E000191E4 /* 學(xué)習(xí)之路UITests */,
                C5A75D7A1F5E7D2E000191E4 /* Products */,
            );
            sourceTree = "<group>";
        };
        C5A75D7A1F5E7D2E000191E4 /* Products */ = {
            isa = PBXGroup;
            children = (
                C5A75D791F5E7D2E000191E4 /* 學(xué)習(xí)之路.app */,
                C5A75D921F5E7D2E000191E4 /* 學(xué)習(xí)之路Tests.xctest */,
                C5A75D9D1F5E7D2E000191E4 /* 學(xué)習(xí)之路UITests.xctest */,
            );
            name = Products;
            sourceTree = "<group>";
        };
        C5A75D7B1F5E7D2E000191E4 /* 學(xué)習(xí)之路 */ = {
            isa = PBXGroup;
            children = (
                C57EAA12212D02D7003D38CD /* Class */,
                C5A75D851F5E7D2E000191E4 /* Main.storyboard */,
                C5A75D881F5E7D2E000191E4 /* Assets.xcassets */,
                C5A75D8A1F5E7D2E000191E4 /* LaunchScreen.storyboard */,
                C5A75D8D1F5E7D2E000191E4 /* Info.plist */,
                C5A75D7C1F5E7D2E000191E4 /* Supporting Files */,
            );
            path = "學(xué)習(xí)之路";
            sourceTree = "<group>";
        };
        C5A75D7C1F5E7D2E000191E4 /* Supporting Files */ = {
            isa = PBXGroup;
            children = (
                C5A75D7D1F5E7D2E000191E4 /* main.m */,
            );
            name = "Supporting Files";
            sourceTree = "<group>";
        };
        C5A75D951F5E7D2E000191E4 /* 學(xué)習(xí)之路Tests */ = {
            isa = PBXGroup;
            children = (
                C5A75D961F5E7D2E000191E4 /* ____Tests.m */,
                C5A75D981F5E7D2E000191E4 /* Info.plist */,
            );
            path = "學(xué)習(xí)之路Tests";
            sourceTree = "<group>";
        };
        C5A75DA01F5E7D2E000191E4 /* 學(xué)習(xí)之路UITests */ = {
            isa = PBXGroup;
            children = (
                C5A75DA11F5E7D2E000191E4 /* ____UITests.m */,
                C5A75DA31F5E7D2E000191E4 /* Info.plist */,
            );
            path = "學(xué)習(xí)之路UITests";
            sourceTree = "<group>";
        };
/* End PBXGroup section */

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

運(yùn)用

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

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

參考

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市冕象,隨后出現(xiàn)的幾起案子稠集,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡未檩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門粟焊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冤狡,“玉大人,你說我怎么就攤上這事项棠”ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵沾乘,是天一觀的道長怜奖。 經(jīng)常有香客問我浑测,道長翅阵,這世上最難降的妖魔是什么歪玲? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮掷匠,結(jié)果婚禮上滥崩,老公的妹妹穿的比我還像新娘。我一直安慰自己讹语,他們只是感情好钙皮,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著顽决,像睡著了一般短条。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上才菠,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天茸时,我揣著相機(jī)與錄音,去河邊找鬼赋访。 笑死可都,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚓耽。 我是一名探鬼主播渠牲,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼步悠!你這毒婦竟也來了签杈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤鼎兽,失蹤者是張志新(化名)和其女友劉穎芹壕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體接奈,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡踢涌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了序宦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睁壁。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖互捌,靈堂內(nèi)的尸體忽然破棺而出潘明,到底是詐尸還是另有隱情,我是刑警寧澤秕噪,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布钳降,位于F島的核電站,受9級(jí)特大地震影響腌巾,放射性物質(zhì)發(fā)生泄漏遂填。R本人自食惡果不足惜铲觉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吓坚。 院中可真熱鬧撵幽,春花似錦、人聲如沸礁击。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽哆窿。三九已至链烈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間挚躯,已是汗流浹背测垛。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秧均,地道東北人食侮。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像目胡,于是被迫代替她去往敵國和親锯七。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Xcode工程文件project.pbxproj小結(jié) 簡介 project.pbxproj 文件被包含于 Xcod...
    凌巔閱讀 27,191評(píng)論 5 72
  • 1誉己、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,981評(píng)論 3 119
  • 自己記得有一次通過網(wǎng)絡(luò)看電影的時(shí)候巨双,中間插播了一條蘋果手機(jī)的廣告噪猾。廣告的畫面大致是這樣的:一個(gè)少年裝扮成的羅密歐坐...
    黯銷凝閱讀 794評(píng)論 0 0
  • 不矯揉造作 大度 熱情 陽光開朗 努力奮斗!
    多啦A夢(mèng)的小日子閱讀 310評(píng)論 0 0
  • 人海茫茫,走出了深澤大山慢宗,覺得視野開闊了很多坪蚁,揮手間,昔日如夢(mèng)镜沽。父母為供我深造真的彎下了脊梁敏晤,讀了幾年的大學(xué),也許...
    喜在心間閱讀 472評(píng)論 0 0