在組件化前期的工作中,我們會(huì)面臨如何管理圖片、音視頻等資源的問(wèn)題。我們需要關(guān)注的問(wèn)題是如何將相應(yīng)的資源和組件一起打包,并保證能夠在組件內(nèi)和組件間的正常使用。以下內(nèi)容均針對(duì)于使用CocoaPods方式組件化對(duì)資源文件管理的討論。
文件管理方式:
1. 集中式管理
將所有的資源文件做成一個(gè)組件,其它相應(yīng)的組件依賴(lài)該資源組件,通過(guò)組件暴露的相關(guān)接口獲取對(duì)應(yīng)的資源。
2. 分散式管理
將資源文件進(jìn)行清晰分類(lèi),對(duì)應(yīng)的資源文件嵌入至對(duì)應(yīng)的組件中,如有共用文件,抽取公共資源組件將公共資源放入該組件中,或者將公共資源直接放至主工程中。
集中式管理與分散式管理優(yōu)缺點(diǎn)對(duì)比:

總結(jié)建議:
集中式管理可方便維護(hù),可對(duì)項(xiàng)目文件進(jìn)行統(tǒng)一管理,此外還可降低文件冗余的概率,可用于較大、耦合嚴(yán)重的項(xiàng)目。倘若是新項(xiàng)目或者組件對(duì)主工程依賴(lài)性不強(qiáng)的組件,可采用分散式管理,將組件的相關(guān)資源與組件綁定。
resources和resource_bundles
resoures和resource_bundles是CocoaPods兩種資源文件引用的方式。
1. resource/resources
resource與resources兩個(gè)屬性功能相同,不同的是resources可以批量指定文件資源,resource只能指定單個(gè)文件資源。
1.1 語(yǔ)法
spec.resource = 'Resources/HockeySDK.bundle'
spec.resources = ['Images/*.png', 'Sounds/*']
1.2 官方描述 Podspec語(yǔ)法官方介紹
resources將指定的資源復(fù)制到目標(biāo)bundle,我們強(qiáng)烈建議開(kāi)發(fā)者使用 resource bundles去構(gòu)建靜態(tài)資源庫(kù)。使用resources屬性?xún)H僅是將指定的文件資源復(fù)制到目標(biāo)bundle,如此Xcode不會(huì)對(duì)相關(guān)資源進(jìn)行優(yōu)化操作。
看完官方描述,我們第一直覺(jué)就會(huì)放棄使用這種方式了。雖然如此,但是我們還是去看看如果使用這種方式具體會(huì)產(chǎn)生哪些影響。
1.3 resource探究
使用pod lib create SCResource_Resources命令創(chuàng)建項(xiàng)目。打開(kāi)Example中的項(xiàng)目,并刪除SCResource_Resources.podspec中無(wú)用的代碼。如下圖所示。

1.3.1 resource不嵌入xcassets文件
選中ReplaceMe.m文件,右鍵Show in Finder,調(diào)至上一級(jí)文件夾,看到Classes和Assets文件夾。我們把ReplaceMe.m刪除,并刪除SCResource_Resources.podspec中的s.source_files,因?yàn)槲覀冊(cè)谫Y源組件中暫時(shí)不用編輯代碼。然后把事先準(zhǔn)備好的圖片資源放入Assets文件夾下,并設(shè)置resource屬性。最終如下圖所示。

終端pod install后,便可以看到圖片資源已經(jīng)被加到Resources文件夾下。

查看資源文件在包中的位置
真機(jī)運(yùn)行,選擇Products文件夾下的SCResource_Resources_Example.app右鍵Show in Finder,選中SCResource_Resources_Example右鍵,選擇顯示包內(nèi)容,就可以看到我們添加的Images文件夾。查看并記錄文件夾的大小。發(fā)現(xiàn)和事先我們準(zhǔn)備的文件夾大小相同,均為21.5M。

資源文件獲取
一般情況下,我們?cè)陧?xiàng)目中獲取圖片都是通過(guò)使用imageNamed:方法去獲取。那么現(xiàn)在我們把圖片資源放在組件中,通過(guò)這樣的方式也能夠獲取嗎?
我們?cè)?code>Example項(xiàng)目中的SCViewController.m的viewDidLoad方法中鍵入如下代碼:

前面我們查看過(guò)
Images最終在APP中的路徑,并且容易找到goodluck_smile圖片的路徑是Images/好運(yùn)墻/goodluck_smile。運(yùn)行項(xiàng)目,發(fā)現(xiàn)我們拿到的image對(duì)象是nil。
很糟糕,我們沒(méi)有獲取到對(duì)應(yīng)的圖片。查看注釋可以知道
imageNamed:是從main bundle中獲取文件資源,那么如果我們把圖片放在主工程中的Images.xcassets文件中,這里的文件在最終的包中的路徑是什么呢?帶著這樣的疑問(wèn),我們簡(jiǎn)單地把一張圖片(goodluck_smile)放入Images.xcassets中,真機(jī)運(yùn)行后,通過(guò)上述查看資源文件在包中的位置的操作方法進(jìn)行查看。
可以看到多出了
Assets.car文件,由此可以知道,Images.xcassets中的圖片資源,最終會(huì)被打包成Assets.car文件,也從側(cè)面可以說(shuō)明Assets.car文件所在的目錄就是main bundle的路徑,那么為什么組件中的圖片沒(méi)有被正常獲取呢?難道是因?yàn)槲覀兟窂絾?wèn)題?前面我們已經(jīng)說(shuō)過(guò)
goodluck_smile圖片是在Images/好運(yùn)墻/ 下,那么我么手動(dòng)拼接試試。為了排除其它影響,我們刪除掉前面在Images.xcassets中的圖片文件,并運(yùn)行。

這時(shí)候,我們正確獲取到了我們想要的圖片?,F(xiàn)在我們可以通過(guò)代碼來(lái)獲取到組件中的圖片了,需要注意的點(diǎn)是需要傳入圖片的相對(duì)路徑,那么在xib中又如何呢?
我們?cè)?code>Main.storyboard中添加一個(gè)UIImageView,并直接設(shè)置goodluck_smile圖片,瞬間就心情大好,因?yàn)榱ⅠR就看到Main.storyboard顯示了對(duì)應(yīng)的圖片。

真機(jī)運(yùn)行,看看會(huì)不會(huì)有什么問(wèn)題。
運(yùn)行后發(fā)現(xiàn),我們?cè)O(shè)置的圖片沒(méi)有正常顯示,那么也是因?yàn)槲覀円顚?xiě)相對(duì)路徑的原因嗎?我們?nèi)ピ囋嚒?/p>

這時(shí)候,發(fā)現(xiàn)Main.storyboard沒(méi)有正常顯示圖片,但是真機(jī)運(yùn)行后,圖片顯示正常。
總結(jié):
使用resource/resources直接存放文件資源時(shí),無(wú)論是通過(guò)代碼獲取圖片,還是在xib中設(shè)置圖片,都需要填寫(xiě)完整的相對(duì)路徑。當(dāng)然如果你想直接通過(guò)設(shè)置圖片名稱(chēng)的方式獲取圖片,那么你必須將圖片直接暴露在resources文件下,不能新建文件對(duì)相關(guān)資源做整理。
1.3.2 resource嵌入xcassets文件
在沒(méi)有組件化時(shí),我們一般把圖片資源都放在Images.xcassets文件內(nèi)管理,其為我們提供了許多優(yōu)化點(diǎn)和一些方便的功能,所以我們可能也希望在組件中也利用這些優(yōu)化和功能。那么在resource中如何通過(guò)xcassets來(lái)管理圖片呢?其實(shí)很簡(jiǎn)單,只需在組件的Assets文件夾下創(chuàng)建Asset Catalog文件,再將圖片資源拖入即可。

這里需要注意的是:在創(chuàng)建Asset Catalog文件后,其目錄可能不在組件的Assets文件下,需要手動(dòng)將其拖入至文件下。

pod install后,對(duì)項(xiàng)目進(jìn)行編譯,如果出現(xiàn)如下錯(cuò)誤,則選擇File -> Workspace Settings -> Build System的New Build System(Default)改為Legacy Build System即可。

使用上文提到的查看資源文件在包中的位置的方式查看文件資源,前面我們也提及到,xcassets文件最后打包進(jìn)APP是會(huì)轉(zhuǎn)成Assets.car文件的,我們找到該文件,并查看該文件的大小。

文件大小變成了69.1M,是原先21.5M的好幾倍,這會(huì)大大增大包的大小。
資源文件獲取
在查看資源文件路徑后,我們發(fā)現(xiàn),其路徑和在主工程中的Images.xcassets在包中的路徑相同,那么可以推測(cè),正常使用相關(guān)方法應(yīng)該可以獲取到資源文件。
同樣在ViewDidLoad方法中鍵入一下代碼,看能否正確獲取圖片資源。斷點(diǎn)運(yùn)行后,發(fā)現(xiàn)可正常獲取。

使用xib方式也一樣,這里就不再截圖,大家可以自己嘗試。
總結(jié):
resource嵌入xcassets文件時(shí),資源文件會(huì)被copy至main bundle中,可以正常獲取資源文件,但是會(huì)造成APP大小變大,因此不建議使用。
2. resource_bundle/resource_bundles
和resource/resources類(lèi)似,resource_bundle/resource_bundles功能相同,區(qū)別在與指定一個(gè)和多個(gè)。
2.1 語(yǔ)法
spec.ios.resource_bundle = { 'MapBox' => 'MapView/Map/Resources/*.png' }
spec.resource_bundles = {
'MapBox' => ['MapView/Map/Resources/*.png'],
'MapBoxOtherResources' => ['MapView/Map/OtherResources/*.png']
}
2.2 官方描述 Podspec語(yǔ)法官方介紹
重點(diǎn)翻譯:強(qiáng)烈建議使用該方式為Pod構(gòu)建靜態(tài)庫(kù),文件資源通過(guò)鍵值匹配資源,bundle的名稱(chēng)應(yīng)該包含Pod的名稱(chēng)來(lái)降低名稱(chēng)沖突的可能性。
2.3 resource_bundle探究
下面同樣通過(guò)是否嵌入xcassets文件來(lái)分析這兩種情況的優(yōu)劣。
2.3.1 resource_bundle不嵌入xcassets文件
不嵌入xcassets文件時(shí),和resource一樣,直接將文件資源拖入至Assets文件夾下,具體參考上文resource不嵌入xcasset文件中的內(nèi)容。然后修改podspec文件制定文件資源的方式,如下圖所示。

pod install,真機(jī)運(yùn)行,查看資源文件在包中的位置,可以看到一個(gè)SCResource_Resources.bundle的文件。查看該文件的大小為21.1M,比原文件略小。

再選擇SCResource_Resources.bundle右鍵顯示包內(nèi)容,可看到我們放進(jìn)去的Images圖片文件夾。

資源文件獲取
前面在resource的章節(jié)中,我們已經(jīng)知道需要通過(guò)拼接資源的相對(duì)路徑才能獲取相應(yīng)的資源,所以我們這里也嘗試看看會(huì)發(fā)生什么。
在ViewDidLoad方法中鍵入下面代碼:
UIImage *image = [UIImage imageNamed:@"SCResource_Resources.bundle/Images/好運(yùn)墻/goodluck_smile"];
斷點(diǎn)查看是否能正常獲取圖片資源。
在xib中同樣這樣拼接,真機(jī)運(yùn)行,看能否正常顯示圖片。

運(yùn)行后,我們可以如預(yù)期一樣獲取資源文件。

總結(jié):
resource_bundle不嵌入xcasset文件,需拼接文件的相對(duì)路徑才能正確獲取圖片資源。
2.3.2 resource_bundle嵌入xcassets文件
嵌入xcassets文件時(shí),也和resource一樣,創(chuàng)建xcassets文件,拖入文件資源,并拖入至Assets文件夾下,具體參考上文resource嵌入xcasset文件中的內(nèi)容。
pod install并真機(jī)運(yùn)行,查看資源文件在包中的位置,我們同樣可以看到SCResource_Resources.bundle文件,查看文件大小,可以看到只有16.8M,比前面所有情況都要小。

再選擇SCResource_Resources.bundle右鍵顯示包內(nèi)容,可看到我們放進(jìn)去的Assets.car文件。與前文的情況一致。
資源文件獲取
在resource的章節(jié)中,如果嵌套xcassets文件,我們可以直接通過(guò)圖片名稱(chēng)來(lái)獲取文件資源,那么這里是不是類(lèi)似呢,我們來(lái)試試。
在viewDidLoad方法中鍵入下面代碼:
UIImage *image = [UIImage imageNamed:@"goodluck_smile"];
運(yùn)行,查看image對(duì)象是否存在。

很遺憾,結(jié)果為nil。那么我們拼接路徑呢?同樣在viewDidLoad方法中鍵入下面代碼:
UIImage *image = [UIImage imageNamed:@"SCResource_Resources.bundle/goodluck_smile"];
運(yùn)行,查看image對(duì)象是否存在。

同樣的結(jié)果,還是nil。
使用這種方式,我們需要換一個(gè)方法去獲取指定資源,我們需要調(diào)用UIImage的imageNamed:inBundle: compatibleWithTraitCollection:方法。指定bundle和圖片的名稱(chēng)即可。
NSString *bundleName = @"SCResource_Resources";
NSString *imageBundlePath = [[NSBundle mainBundle] pathForResource:bundleName ofType:@"bundle"];
NSBundle *imageBundle = [NSBundle bundleWithPath:imageBundlePath];
UIImage *image = [UIImage imageNamed:@"goodluck_smile" inBundle:imageBundle compatibleWithTraitCollection:nil];
運(yùn)行,查看image對(duì)象是否存在

運(yùn)行結(jié)果如預(yù)期,可獲取對(duì)應(yīng)文件資源。
在xib中如何設(shè)置呢?可以添加分類(lèi)暴露bundleName和imageName使用IBInspectable修飾,調(diào)用imageNamed:inBundle: compatibleWithTraitCollection:方法。
總結(jié):
resource_bundle嵌入xcasset文件,包文件大小相對(duì)于其它情況較小,但獲取文件資源時(shí),需要封裝方法調(diào)用UIImage的imageNamed:inBundle: compatibleWithTraitCollection:方法。
總結(jié)
使用CocoaPods方式組件化,對(duì)文件資源進(jìn)行管理,建議使用resource_bundle/resource_bundles嵌入xcassets文件的方式。這樣一來(lái)可以使用xcassets的一些特性和優(yōu)化,也能夠在一定程度上減小包的體積。