[iOS] 解決升級(jí)到Xcode13編譯失敗的問題

前言:最近把Xcode升級(jí)到了Xcode13,發(fā)現(xiàn)老項(xiàng)目突然運(yùn)行不起來了肩榕,原來是老項(xiàng)目使用的還是老的構(gòu)建系統(tǒng)Legacy Build System,沒有使用New Build System。之前只是簡(jiǎn)單有過了解乐横,現(xiàn)在再深入了解一下兩個(gè)構(gòu)建系統(tǒng)的區(qū)別拥褂。

1. Xcode13編譯報(bào)錯(cuò)解決

先來解決下Xcode13編譯報(bào)錯(cuò)的問題抡驼,報(bào)錯(cuò)信息如下:

Showing All Messages
: The Legacy Build System will be removed in a future release. You can configure the selected build system and this deprecation message in File > Workspace Settings.

蘋果還是貼心的告訴我們?cè)趺慈バ薷牧耍蜷_File -> Workspace Settings肿仑,去掉這個(gè)報(bào)錯(cuò)信息致盟,如下圖所示:

image.png

這樣就可以解決編譯報(bào)錯(cuò)的問題了碎税,但是如果工作不太忙,建議還是切換成蘋果提供的New Build System馏锡。這樣可能會(huì)帶來一些其它未知的問題雷蹂,不過我們也都是在不斷解決問題中成長(zhǎng)的。

2. New Build System

2.1 簡(jiǎn)介

在之前Xcode9發(fā)布的時(shí)候杯道,Apple在Build System上提供了新版本的構(gòu)建系統(tǒng)New Build System匪煌,在WWDC2017上的介紹很簡(jiǎn)單,但是足夠覆蓋了該構(gòu)建系統(tǒng)的優(yōu)點(diǎn):降低構(gòu)建開銷党巾,尤其是可以降低大型項(xiàng)目的構(gòu)建開銷萎庭。

對(duì)于開發(fā)者,蘋果提供了足夠的過渡時(shí)間(你看我們的項(xiàng)目到現(xiàn)在才使用了New Build System)齿拂,在Xcode9中驳规,該構(gòu)建系統(tǒng)沒有設(shè)置為默認(rèn)的構(gòu)建系統(tǒng),而在Xcode10中署海,蘋果將該系統(tǒng)設(shè)置為默認(rèn)的構(gòu)建系統(tǒng)吗购,Xcode13中,如果沒有使用New Build System砸狞,則會(huì)報(bào)錯(cuò)了捻勉。我們可以通過Xcode->File->Project Settings/WorkSpace Settings->Build System在新舊構(gòu)建系統(tǒng)之間進(jìn)行切換,如下圖所示:

image.png

2.2 新舊構(gòu)建系統(tǒng)的對(duì)比

2.2.1 項(xiàng)目依賴關(guān)系

實(shí)際開發(fā)中刀森,項(xiàng)目可能會(huì)依賴多個(gè)其它的工程或者三方庫踱启,這些依賴分為兩部分:

  • Target Dependencies
    當(dāng)前Target所依賴的其它Target,被依賴的Target必須在本Target構(gòu)建之前就構(gòu)建完成研底,除此之外沒有任何關(guān)聯(lián)禽捆。

  • Link Binary With Libraries
    指最終要Link到Product中的文件,同時(shí)在Link到Product中時(shí)飘哨,需要保證文件存在胚想,這就要求在構(gòu)建Target時(shí)該項(xiàng)目下的文件必須提前構(gòu)建完成

也就是Target無論通過哪種依賴芽隆,都需要保證被依賴的內(nèi)容在Target構(gòu)建之前久已經(jīng)被構(gòu)建成功浊服。

我們使用WWDC演示中提供的項(xiàng)目結(jié)構(gòu)(Tests Target)用來對(duì)比兩種構(gòu)建系統(tǒng),如下圖所示:

image.png

其中連線為依賴關(guān)系胚吁,箭頭所指為被依賴target牙躺。

2.2.2 舊構(gòu)建系統(tǒng) Legacy Build System

對(duì)于程序來說,我們要構(gòu)建其中一個(gè)Target腕扶,可以確定以下幾點(diǎn):

  • 所需要構(gòu)建的所有Target
  • Target之間的依賴關(guān)系
  • Target構(gòu)建的順序

以上圖的項(xiàng)目結(jié)構(gòu)為例孽拷,我們?nèi)绻霕?gòu)建Tests,那么圖中所有Target都需要進(jìn)行構(gòu)建半抱,對(duì)于構(gòu)建順序圖可以如下所示:


image.png

從上圖中可以看出脓恕,Target必須要等到其依賴的Target構(gòu)建完成之后才被構(gòu)建膜宋,整體是一個(gè)串行編譯的過程。而New Build System優(yōu)化的核心思想炼幔,就是采用并行編譯秋茫,提高編譯效率,減少編譯時(shí)間乃秀。

2.2.3 新構(gòu)建系統(tǒng) New Build System
2.2.3.1 依賴拆分

對(duì)于一開始的項(xiàng)目依賴圖肛著,我們可以先去對(duì)Tests的依賴關(guān)系進(jìn)行摘取,如下圖所示:

image.png

可以看出Tests的依賴可以分為三種跺讯,分別對(duì)應(yīng)Game枢贿、Shaders、Utilities刀脏,如下圖所示:

image.png

Tests的構(gòu)建就不用等到Game局荚、Shaders、Utilities三個(gè)Target都構(gòu)建完成才進(jìn)行火本。對(duì)于每一部分的構(gòu)建可以等到對(duì)應(yīng)Target構(gòu)建完成之后就可以立即進(jìn)行,如下圖所示:

image.png

由此可見聪建,通過內(nèi)容拆分钙畔,我們可以并行的進(jìn)行構(gòu)建,從而降低構(gòu)建時(shí)間金麸。

2.2.3.2 優(yōu)先構(gòu)建有用部分

再來看下ShadersUtilities之間的依賴擎析,Utilities會(huì)提供一些工具方法,而Shaders會(huì)使用到Utilities中的一些方法挥下,但是Shaders并不會(huì)全部使用揍魂,只會(huì)用到Utilties的一部分。這就為構(gòu)建優(yōu)化提供了思路:Shaders的構(gòu)建可以在Utilities中與之有關(guān)的內(nèi)容構(gòu)建完成之后就可以進(jìn)行棚瘟,如圖所示:

image.png

雖然Utilities存在對(duì)Physics的依賴现斋,但是理想狀態(tài)下,如果提取的Code Gen不存在對(duì)Physics的依賴偎蘸,那Code Gen的編譯就可以提前到與Physics一個(gè)時(shí)間點(diǎn)庄蹋,如圖:

image.png

通過內(nèi)容提取,可以將某些內(nèi)容的構(gòu)建時(shí)間點(diǎn)提前迷雪,從而減少整體構(gòu)建時(shí)間限书。

2.2.3.3 遺留依賴清理

在做項(xiàng)目?jī)?yōu)化的時(shí)候,我們會(huì)刪除一些之前的無用代碼章咧,最新的代碼可能不會(huì)依賴之前的某些框架倦西,但是對(duì)于框架依賴的設(shè)置可能由于遺忘而遺留下來,我們可以通過清理這部分遺留無用依賴來加快構(gòu)建速度赁严。

在本例中扰柠,假設(shè)經(jīng)過長(zhǎng)時(shí)間的迭代粉铐,Utilities中的內(nèi)容已經(jīng)不存在對(duì)Physics框架的任何依賴,此時(shí)如果我們清理掉Utilities對(duì)Physics依賴的設(shè)置耻矮,那么Utilities的構(gòu)建就不必等到Physics完成了秦躯,如圖:

及時(shí)清理遺留的無用依賴設(shè)置,可以提前某些模塊的編譯時(shí)間點(diǎn)裆装,進(jìn)而減少整體構(gòu)建時(shí)間踱承。

2.2.3.4 新版Xcode的特新
  • 提前編譯源碼的時(shí)間點(diǎn)
  • 一旦所依賴的內(nèi)容構(gòu)建完成就可以開始構(gòu)建,無需等待全部依賴構(gòu)建
  • 優(yōu)化Run Script phases的執(zhí)行來減少編譯工作

3. Run Script phases的優(yōu)化

New Build System中哨免,優(yōu)化了Run Script phases的執(zhí)行工作茎活,總得來說,就是為Run Script phases引入了依賴的概念琢唾,進(jìn)而將Run Script phases放入并行構(gòu)建中载荔,從而加快構(gòu)建速度。那么Run Script phases的依賴關(guān)系如何確定呢采桃?

3.1 Input Files/Output Files

New Build System中懒熙,將Input FilesOutput Files作為該Run Script phase的依賴關(guān)系,構(gòu)建系統(tǒng)會(huì)根據(jù)這些文件來確定Run Script phases在構(gòu)建過程中的執(zhí)行時(shí)間點(diǎn)普办,具體原則如下:

3.2 執(zhí)行的前提
  • 沒有指定Input File
  • Input File內(nèi)容改變
  • Output File丟失
3.3 執(zhí)行時(shí)間點(diǎn)
  • 若沒有指定Input File工扎,執(zhí)行時(shí)間點(diǎn)會(huì)在構(gòu)建最開始
  • 若指定了Input File,則需要保證Input File構(gòu)建完成

3.2 Input Files List/Output Files List

蘋果為了避免開發(fā)者在執(zhí)行腳本時(shí)可能指定過多的Input FilesOutput Files衔蹲,新增了Input Files ListOutput Files List肢娘,在這兩個(gè)參數(shù)中,可以指定一個(gè)后綴為.xcfilelist的文件舆驶,在該文件中列舉所需依賴的Input FilesOutput Files橱健,文件內(nèi)容格式如下:

image.png

3.3 New Build System可能引起的問題

開發(fā)者可能在項(xiàng)目中設(shè)置一些Script,在其中可能會(huì)做一些Build version沙廉、App Icon等的設(shè)置拘荡,這些腳本在舊的串行構(gòu)建系統(tǒng)中會(huì)在最后執(zhí)行,最終完成所需內(nèi)容的替換撬陵,達(dá)到所需目的俱病。

但是在新構(gòu)建系統(tǒng)中,若不做特殊設(shè)置袱结,該腳本會(huì)在并行構(gòu)建的開始階段就執(zhí)行亮隙,從而無法保證最終的替換能夠生效(可能會(huì)被其余構(gòu)建過程替換)。

解決方式就是為腳本設(shè)置好依賴關(guān)系垢夹,從而保證腳本執(zhí)行在Target構(gòu)建之后溢吻。我們知道,Process Info.plist過程會(huì)為.app文件生成Info.plst文件并進(jìn)行初始化,我們可以將該文件設(shè)置為Run Script phasesInput Files促王,保證腳本的執(zhí)行時(shí)間點(diǎn)在Info.plist更改之后犀盟,進(jìn)而保證腳本的執(zhí)行結(jié)果有效。

4. 遇到的問題

在切換New Build System時(shí)確實(shí)遇到了一個(gè)問題蝇狼,報(bào)錯(cuò)信息如下:

image.png

產(chǎn)生這個(gè)問題的原因是多個(gè)命令生成了.car文件阅畴。為了研究這個(gè)問題,還需要了解下Pod庫圖片資源的引用方式迅耘。

5. Pod庫圖片資源的引用方式

包括兩種:resource_bundlesresources贱枣。

5.1 resource_bundles

resource_bundles允許定義當(dāng)前 Pod 庫的資源包的名稱和文件。用 hash 的形式來聲明颤专,key 是 bundle 的名稱纽哥,value 是需要包括的文件的通配 patterns。

官方推薦使用resource_bundles方式引用圖片資源栖秕,同時(shí)建議 bundle 的名稱至少應(yīng)該包括 Pod 庫的名稱春塌,可以盡量減少同名沖突。

使用方式如下:

# LibResources 是可以自定義的Bundle的名字
# Resources 是創(chuàng)建的Pod庫的名稱
s.resource_bundles = {
     'LibResources' => ['Resources/Assets/**/*.png']
}

pod install之后簇捍,構(gòu)建一下只壳,打開Products下的.app文件,顯示包內(nèi)容暑塑。

5.1.1 靜態(tài)庫.a形式
  • 如果使用的是.a吼句,靜態(tài)pod庫的依賴方式,可以看到.app下會(huì)有一個(gè)LibResources.bundle文件梯投,存放Pod庫用的圖片資源文件命辖,如下圖:
    截屏2021-12-27 下午8.50.55.png
5.1.2 動(dòng)態(tài)庫.framework形式
  • 如果使用的是.framework動(dòng)態(tài)庫的依賴方式况毅,可以看到在.app內(nèi)會(huì)有一個(gè)和Pod同名的Resources.framework分蓖,里面有一個(gè)LibResources.bundle文件,如圖:
    截屏2021-12-27 下午9.16.33.png

因?yàn)镻od可能作為動(dòng)態(tài)庫或者靜態(tài)庫的形式提供給工程使用尔许,為了兼容這兩種情況么鹤,使用bundleForClass:來獲取Pod的bundle,當(dāng)Pod作為靜態(tài)庫時(shí)味廊,該方法返回的是mainBundle蒸甜,當(dāng)Pod作為動(dòng)態(tài)庫時(shí),該方法返回的就是動(dòng)態(tài)庫本身余佛。

所以在使用resource_bundles這種方式引用pod庫中的圖片資源時(shí)柠新,在pod庫中使用圖片的代碼如下:

+ (nullable UIImage *)imageName:(NSString *)name {
    static NSBundle *resourceBundle = nil;
    if (resourceBundle == nil) {
        resourceBundle = [NSBundle bundleWithPath:[[NSBundle bundleForClass:[xxxx(pod庫任意類名) class]] pathForResource:@"xxx(bundle名稱)" ofType:@"bundle"]];
    }
    UIImage *image = [UIImage imageNamed:name inBundle:resourceBundle compatibleWithTraitCollection:nil];
    return image;
}

5.2 resources

使用 resources 來指定資源,被指定的資源只會(huì)簡(jiǎn)單的被 copy 到目標(biāo)工程中(主工程)辉巡。

官方認(rèn)為用 resources 是無法避免同名資源文件的沖突的恨憎,同時(shí),Xcode 也不會(huì)對(duì)這些資源做優(yōu)化。

使用示例:

spec.resources = 'Images/*'
5.2.1 以靜態(tài)庫.a的形式

如果pod庫是以靜態(tài)庫.a文件的形式提供的憔恳,這樣只是會(huì)拷貝到.app內(nèi)瓤荔,如下圖所示:

截屏2021-12-28 上午11.34.05.png

這種圖片資源引用方式跟我們直接把圖片放到主工程項(xiàng)目下的存放方式是一樣的,都是直接copy到.app下钥组,所以在pod庫中输硝,可以使用imageNamed:方法獲取圖片,在主工程中也可以通過imageNamed:方法獲取pod庫中的圖片程梦,如下:

UIImage *image = [UIImage imageNamed:@"share_bgImage"];

這可能會(huì)導(dǎo)致同名資源文件的沖突点把,如果主工程中也有一個(gè)圖片名字為share_bgImage,編譯時(shí)就會(huì)報(bào)錯(cuò)作烟,如下圖所示:

截屏2021-12-28 上午11.40.19.png

5.2.2 以動(dòng)態(tài)庫.framework的形式

最后也會(huì)存在.app下和pod庫同名的.framework文件夾下愉粤,如下圖所示:

截屏2021-12-28 上午11.55.13.png

在pod庫中使用這個(gè)圖片時(shí),需要先獲取到圖片所在的bundle拿撩,再根據(jù)圖片名字獲取圖片衣厘,如下所示:

NSBundle *bundle = [NSBundle bundleForClass:[self class]];
if (bundle) {
     return [UIImage imageNamed:name inBundle:bundle compatibleWithTraitCollection:nil];
}

所以在構(gòu)建pod庫時(shí)還是使用resource_bundles這種圖片引用方式。

綜上可知压恒,不同的圖片資源引用方式和不同的pod庫使用形式導(dǎo)致最后圖片資源的位置是不一樣的影暴,如下所示:

- 動(dòng)態(tài)庫.framework 靜態(tài)庫.a
resources xxx.app/xxx.framework xxx.app
resource_bundles xxx.app/xxx.framework/xxx.bundle xxx.app/xxx.bundle

5.3 在pod庫中使用.xcassets管理圖片

pod庫中使用.xcassets管理不同分辨率的圖片會(huì)更加方便,使用方式如下:

s.resources = ["Resources/XCA/*.xcassets"]

但是pod在使用.xcassets探赫,編譯的時(shí)候會(huì)生成一個(gè)Assets.car文件型宙,可以在Build Phase -> [CP] Copy Pods Resources -> Output File Lists下看到:

${PODS_ROOT}/Target Support Files/Pods-HTDemo/Pods-HTDemo-resources-${CONFIGURATION}-output-files.xcfilelist

打開這個(gè)文件,在最下方能看到會(huì)生成一個(gè)Assets.car文件伦吠,如下圖所示:

截屏2021-12-28 下午2.46.53.png

而我們的主項(xiàng)目也會(huì)生成一個(gè)Assets.car文件妆兑,那么就可能會(huì)產(chǎn)生沖突,編譯報(bào)錯(cuò)毛仪,也就是上面第4點(diǎn)中遇到的編譯錯(cuò)誤搁嗓。

為什么說可能會(huì)產(chǎn)生錯(cuò)誤?因?yàn)樯厦嫖覀冎?code>resources和resource_bundles和pod庫的使用方式會(huì)導(dǎo)致圖片資源存放的位置發(fā)生變化箱靴,如果我們使用了resources管理.xcassets腺逛,并且pod庫是以靜態(tài)庫.a的方式提供的,那就會(huì)導(dǎo)致編譯報(bào)錯(cuò)衡怀。

pod庫生成的Assets.car文件會(huì)存放到.app下棍矛,主工程生成的Assets.car也會(huì)存放到.app下,產(chǎn)生沖突抛杨,就報(bào)錯(cuò)了够委。

解決這個(gè)問題,有兩種方案怖现,第一種就是使用resource_bundles茁帽,

s. resource_bundles = { "bundleName" => ["Resources/XCA/*.xcassets"]}

第二種就是屏蔽[CP] Copy Pods Resources下的輸入和輸出路徑,在podfile中加入:

install! 'cocoapods',
         :disable_input_output_paths => false

重新pod install即可,可以看到[CP] Copy Pods Resources下的輸入和輸出路徑都沒有了:

截屏2021-12-28 下午2.51.52.png

那此時(shí)圖片去哪了呢脐雪?圖片會(huì)合并到主項(xiàng)目生成的Assets.car中厌小,可以把主項(xiàng)目中的Assets.xcassets中的圖片刪除,pod庫中的Assets.xcassets中的圖片保留試一試战秋,最后生成的.app下還是會(huì)有一個(gè)Assets.car文件璧亚。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市脂信,隨后出現(xiàn)的幾起案子癣蟋,更是在濱河造成了極大的恐慌,老刑警劉巖狰闪,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疯搅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡埋泵,警方通過查閱死者的電腦和手機(jī)幔欧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丽声,“玉大人礁蔗,你說我怎么就攤上這事⊙闵纾” “怎么了浴井?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)霉撵。 經(jīng)常有香客問我磺浙,道長(zhǎng),這世上最難降的妖魔是什么徒坡? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任撕氧,我火速辦了婚禮,結(jié)果婚禮上崭参,老公的妹妹穿的比我還像新娘呵曹。我一直安慰自己款咖,他們只是感情好何暮,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著铐殃,像睡著了一般海洼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上富腊,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天坏逢,我揣著相機(jī)與錄音,去河邊找鬼。 笑死是整,一個(gè)胖子當(dāng)著我的面吹牛肖揣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播浮入,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼龙优,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了事秀?” 一聲冷哼從身側(cè)響起彤断,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎易迹,沒想到半個(gè)月后宰衙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡睹欲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年供炼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窘疮。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劲蜻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出考余,到底是詐尸還是另有隱情先嬉,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布楚堤,位于F島的核電站疫蔓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏身冬。R本人自食惡果不足惜衅胀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酥筝。 院中可真熱鬧滚躯,春花似錦、人聲如沸嘿歌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宙帝。三九已至丧凤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間步脓,已是汗流浹背愿待。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工浩螺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仍侥。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓要出,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親农渊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子厨幻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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