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 ,不然没法导入!
相关文章
相关标签/搜索