CocoaPods的資源管理和Asset Catalog優化

針對目前公司業務子庫資源引用方式各異,包體大小超出正常值的問題,做出了一些調研.同時網上的部分說法可能隨着時間的推移和cocoapods的更新已通過時或不可用.本文展現了資源引用的各類方式,也防止你們走彎路.css

這篇文章介紹了關於CocoaPods的資源管理行爲,對於Pod庫做者是必須瞭解的知識。同時介紹了CocoaPods使用Asset Catalog的注意事項。若是已經瞭解某方面知識,能夠大體略過直接看結論。html

Asset Catalog和App Thinning

Asset Catalog,是Xcode提供的一項圖片資源管理方式。每一個Asset表示一個圖片資源,可是能夠對應一個或者多個實際PNG圖,好比能夠提供@1x, @2x, @3x多張尺寸的圖以適配;,還能夠經過指定日間和夜間不一樣Appearances的兩套圖片。json

這種資源,在編譯時會被壓縮,而後在App運行時,能夠經過API動態根據設備scale factor來選擇對應的真實的圖片渲染。xcode

App Thinning,是蘋果平臺(iOS/tvOS/watchOS)上的一個用於優化App包下載資源大小的方案。在App包提交上傳到App Store後,蘋果後臺服務器,會對不一樣的設備,根據設備的scale factor,從新把App包進行精簡,這樣不一樣設備從App Store下載須要的容量不一樣,3x設備不須要同時下載1x和2x的圖。ruby

可是,這套機制直接基於Asset Catalog,換言之,只有在Asset Catalog中引入的圖片,才能夠利用這套App Thinning。直接拷貝到App Bundle中的散落圖片,全部設備仍是都會所有下載。所以如何儘可能提高Asset Catalog利用率,是一個很大的包大小優化點。服務器

CocoaPods的資源管理

CocoaPods是一個構建工具,它徹底基於Pods的spec文件規則,在Podfile引入後,生成對應構建Xcode Target。也就是它是一個聲明式構建工具(區別於Makefile這種過程式的構建工具)。對於資源的管理,目前有兩個方式進行聲明並引入,即resourcesresource_bundles,參考podspec syntaxapp

雖然Podspec中包含全部待構建庫的聲明,但於CocoaPods也會根據Podfile的配置,動態調整最終的Xcode工程的配置,根據是否開啓use_framework!,如下的資源聲明最終的行爲有所不一樣,這裏分開介紹。ide

爲了測試使用場景,咱們新建了三個工程 工程目錄以下:工具

主工程爲 CatalogTest ,測試

資源工程爲: ResourcesTest,ResourcesBubdlesTest

ResourcesTest 裏包含資源文件:

ResourcesBubdlesTest包含資源文件:

不使用use_framework!

第一個測試:使用通配符導入 /Assets/**/*'

使用 resources
  • 導入方式 s.resources = 'ResourcesTest/Assets/**/*'
使用resource_bundles
  • 導入方式

    s.resource_bundles = {
         'ResourcesBubdlesTest' => ['ResourcesBubdlesTest/Assets/**/*']
       }
    複製代碼
app 目錄結構

而經過resources 導入的文件直接會在根目錄導入兩份而且不會 通過Xcode的資源優化

bundle也會以bundle和裏面的全部文件在根目錄導入

經過resource_bundles 形式導入的文件會生成一個自命名的 budle ,bundle裏包含文件

結論: 按照**/*導入的資源文件不管哪一種形式都存在被重複導入的問題,這種懶省事的方式應該被制止

第二個測試:使用通配符導入 /Assets/*.xcasset',/Assets/*.bundle

ResourcesTest:

xcasseets併入主工程 .car

Bundle 併入主目錄

s.resources = ['ResourcesTest/Assets/*.xcassets',
 'ResourcesTest/Assets/*.bundle'
 ]
複製代碼

ResourcesBubdlesTest:

注意 bundle必須有本身的命名空間,不然仍是以文件形式導入

Bundle 文件會以命名空間爲名字放在主目錄下

s.resource_bundles = {
     'ResourcesBubdlesTest' => ['ResourcesBubdlesTest/Assets/*.xcassets'],
     'BlankLoading1' => ['ResourcesBubdlesTest/Assets/BlankLoading.bundle']
   }
複製代碼

使用use_framework!

爲了測試使用場景,咱們新建了三個工程 工程目錄以下:

主工程爲 CatalogTest ,

資源工程爲: ResourcesTest,ResourcesBubdlesTest

ResourcesTest 裏包含資源文件:

ResourcesBubdlesTest包含資源文件:

第一個測試:使用通配符導入 /Assets/**/*'

使用 resources
  • 導入方式 s.resources = 'ResourcesTest/Assets/**/*'

    xcassets 生成.car, bundle文件依然雙份

使用resource_bundles
  • 導入方式

    s.resource_bundles = {
         'ResourcesBubdlesTest' => ['ResourcesBubdlesTest/Assets/**/*']
       }
    複製代碼
app 目錄結構

第二個測試:使用通配符導入 /Assets/*.xcasset',/Assets/*.bundle

每一個子工程有本身的 framework,也就是有本身的命名空間,不會出現命名衝突等問題

ResourcesTest:

xcasseets併入主工程 .car

Bundle 併入主目錄

s.resources = ['ResourcesTest/Assets/*.xcassets',

 'ResourcesTest/Assets/*.bundle'

 ]
複製代碼

ResourcesBubdlesTest:

s.resource_bundles = {
     'ResourcesBubdlesTest' => ['ResourcesBubdlesTest/Assets/*.xcassets'],
     'BlankLoading1' => ['ResourcesBubdlesTest/Assets/BlankLoading.bundle']
   }
複製代碼

結論

不管任何場景,禁止 使用 podspecName/assers/**/*形式引入資源文件,存在嚴重的資源重複引入問題,會顯著增長包體大小!沒法享有任何 Xcode的優化.

使用 resources

s.resources = ['ResourcesTest/Assets/*.xcassets',
 'ResourcesTest/Assets/*.bundle'
 ]
複製代碼

使用 resource_bundles

s.resource_bundles = {
     'ResourcesBubdlesTest' => ['ResourcesBubdlesTest/Assets/*.xcassets'],
     'BlankLoading1' => ['ResourcesBubdlesTest/Assets/BlankLoading.bundle']
   }
複製代碼

不使用use_framework!

當不使用use_framework!時,最終對Pod庫,會建立單獨的靜態連接庫.a的Target,而後CocoaPods會對主工程App Target增長本身寫的腳原本幫助咱們拷貝Pod的資源。

resources*.xcassets資源會拷貝進入主目錄Assets.car,*.bundle文件放入主目錄下!

優勢:

最簡單暴力,並且因爲固定了資源的路徑在根路徑上,若是先前在主工程目錄中使用的代碼,不須要更改一行便可繼續使用。

缺點:

  1. 命名衝突問題,存在同名文件,會進行暴力合併!致使一個被替換掉.。所以,這要求全部資源文件命名自己,加入特定的前綴以免衝突。相似的不止是圖片,全部資源如bundle, js, css均可能存在這個問題,難以排查。並且因爲這種拷貝到根路徑的機制,這個問題不可從根源避免。

resource_bundles文件*.xcassets會放入 命名空間.bundle下的Assets.car,*.bundle放入主目錄下的命名空間.bundle

優勢:

解決了部分命名衝突問題,*.xcassets存在本身的命名空間.

缺點:

  1. bundle文件依然可能命名衝突
  2. 因爲最終的資源文件增減了一級父文件夾.因此資源引用方式要作出改變

使用use_framework!

當使用了use_framework!以後,CocoaPods會對每一個Pod單獨創建一個動態連接庫的Target,每一個Pod最後會直接以Framework集成到App中。而資源方面,因爲Framework自己就能承載資源,全部的資源都會被拷貝到Framework文件夾中而再也不使用單獨的腳本處理。

優勢:

  1. 不會存在命名衝突。由於在使用use_framework!的狀況下,因爲資源自己被拷貝到Framework中,已經能最大程度減小衝突,所以這時候通常不須要考慮名稱衝突問題.

  2. 都會使用Xcode的優化

缺點: 因爲最終的資源文件增減了一級父文件夾.因此資源引用方式要作出改變

採用方案:

結合咱們公司目前現狀不能使用動態庫.因此使用以下方案:

  1. 業務層使用resources導入資源

    • 資源文件使用 *.xcassets *.bundle *.text方式導入,可是各子庫文件會存在命名衝突問題,好處是不用改變資源引用方式,

    • 同時約定,各子庫的資源添加子庫名字縮寫的前綴,避免暴力拷貝問題

  2. 基礎服務層,和工具層使用resource_bundles

    • 防止出現基礎服務裏的資源被暴力替換
    • 注意 .plist .json .html 須要建一個bundleName.bunlde ,不然沒法導入!
相關文章
相關標籤/搜索