iOS组件化-资源管理

在组件化前期的工作中,我们会面临如何管理图片、音视频等资源的问题。我们需要关注的问题是如何将相应的资源和组件一起打包,并保证能够在组件内和组件间的正常使用。以下内容均针对于使用CocoaPods方式组件化对资源文件管理的讨论。

文件管理方式:

1. 集中式管理

将所有的资源文件做成一个组件,其它相应的组件依赖该资源组件,通过组件暴露的相关接口获取对应的资源。

2. 分散式管理

将资源文件进行清晰分类,对应的资源文件嵌入至对应的组件中,如有共用文件,抽取公共资源组件将公共资源放入该组件中,或者将公共资源直接放至主工程中。

集中式管理与分散式管理优缺点对比:
总结建议:

集中式管理可方便维护,可对项目文件进行统一管理,此外还可降低文件冗余的概率,可用于较大、耦合严重的项目。倘若是新项目或者组件对主工程依赖性不强的组件,可采用分散式管理,将组件的相关资源与组件绑定。

resources和resource_bundles

resouresresource_bundlesCocoaPods两种资源文件引用的方式。

1. resource/resources

resourceresources两个属性功能相同,不同的是resources可以批量指定文件资源,resource只能指定单个文件资源。

1.1 语法
spec.resource = 'Resources/HockeySDK.bundle'
spec.resources = ['Images/*.png', 'Sounds/*']
1.2 官方描述 Podspec语法官方介绍

resources将指定的资源复制到目标bundle,我们强烈建议开发者使用 resource bundles去构建静态资源库。使用resources属性仅仅是将指定的文件资源复制到目标bundle,如此Xcode不会对相关资源进行优化操作。

看完官方描述,我们第一直觉就会放弃使用这种方式了。虽然如此,但是我们还是去看看如果使用这种方式具体会产生哪些影响。

1.3 resource探究

使用pod lib create SCResource_Resources命令创建项目。打开Example中的项目,并删除SCResource_Resources.podspec中无用的代码。如下图所示。

1.3.1 resource不嵌入xcassets文件

选中ReplaceMe.m文件,右键Show in Finder,调至上一级文件夹,看到ClassesAssets文件夹。我们把ReplaceMe.m删除,并删除SCResource_Resources.podspec中的s.source_files,因为我们在资源组件中暂时不用编辑代码。然后把事先准备好的图片资源放入Assets文件夹下,并设置resource属性。最终如下图所示。

终端pod install后,便可以看到图片资源已经被加到Resources文件夹下。

查看资源文件在包中的位置

真机运行,选择Products文件夹下的SCResource_Resources_Example.app右键Show in Finder,选中SCResource_Resources_Example右键,选择显示包内容,就可以看到我们添加的Images文件夹。查看并记录文件夹的大小。发现和事先我们准备的文件夹大小相同,均为21.5M。

资源文件获取

一般情况下,我们在项目中获取图片都是通过使用imageNamed:方法去获取。那么现在我们把图片资源放在组件中,通过这样的方式也能够获取吗?
我们在Example项目中的SCViewController.mviewDidLoad方法中键入如下代码:


前面我们查看过Images最终在APP中的路径,并且容易找到goodluck_smile图片的路径是Images/好运墙/goodluck_smile。运行项目,发现我们拿到的image对象是nil

很糟糕,我们没有获取到对应的图片。查看注释可以知道imageNamed:是从main bundle中获取文件资源,那么如果我们把图片放在主工程中的Images.xcassets文件中,这里的文件在最终的包中的路径是什么呢?带着这样的疑问,我们简单地把一张图片(goodluck_smile)放入Images.xcassets中,真机运行后,通过上述查看资源文件在包中的位置的操作方法进行查看。

可以看到多出了Assets.car文件,由此可以知道,Images.xcassets中的图片资源,最终会被打包成Assets.car文件,也从侧面可以说明Assets.car文件所在的目录就是main bundle的路径,那么为什么组件中的图片没有被正常获取呢?难道是因为我们路径问题?
前面我们已经说过goodluck_smile图片是在Images/好运墙/ 下,那么我么手动拼接试试。为了排除其它影响,我们删除掉前面在Images.xcassets中的图片文件,并运行。

这时候,我们正确获取到了我们想要的图片。现在我们可以通过代码来获取到组件中的图片了,需要注意的点是需要传入图片的相对路径,那么在xib中又如何呢?
我们再Main.storyboard中添加一个UIImageView,并直接设置goodluck_smile图片,瞬间就心情大好,因为立马就看到Main.storyboard显示了对应的图片。

真机运行,看看会不会有什么问题。
运行后发现,我们设置的图片没有正常显示,那么也是因为我们要填写相对路径的原因吗?我们去试试。

这时候,发现Main.storyboard没有正常显示图片,但是真机运行后,图片显示正常。

总结:

使用resource/resources直接存放文件资源时,无论是通过代码获取图片,还是在xib中设置图片,都需要填写完整的相对路径。当然如果你想直接通过设置图片名称的方式获取图片,那么你必须将图片直接暴露在resources文件下,不能新建文件对相关资源做整理。

1.3.2 resource嵌入xcassets文件

在没有组件化时,我们一般把图片资源都放在Images.xcassets文件内管理,其为我们提供了许多优化点和一些方便的功能,所以我们可能也希望在组件中也利用这些优化和功能。那么在resource中如何通过xcassets来管理图片呢?其实很简单,只需在组件的Assets文件夹下创建Asset Catalog文件,再将图片资源拖入即可。

这里需要注意的是:在创建Asset Catalog文件后,其目录可能不在组件的Assets文件下,需要手动将其拖入至文件下。

pod install后,对项目进行编译,如果出现如下错误,则选择File -> Workspace Settings -> Build SystemNew Build System(Default)改为Legacy Build System即可。

使用上文提到的查看资源文件在包中的位置的方式查看文件资源,前面我们也提及到,xcassets文件最后打包进APP是会转成Assets.car文件的,我们找到该文件,并查看该文件的大小。

文件大小变成了69.1M,是原先21.5M的好几倍,这会大大增大包的大小。

资源文件获取

在查看资源文件路径后,我们发现,其路径和在主工程中的Images.xcassets在包中的路径相同,那么可以推测,正常使用相关方法应该可以获取到资源文件。

同样在ViewDidLoad方法中键入一下代码,看能否正确获取图片资源。断点运行后,发现可正常获取。

使用xib方式也一样,这里就不再截图,大家可以自己尝试。

总结:

resource嵌入xcassets文件时,资源文件会被copymain bundle中,可以正常获取资源文件,但是会造成APP大小变大,因此不建议使用。

2. resource_bundle/resource_bundles

resource/resources类似,resource_bundle/resource_bundles功能相同,区别在与指定一个和多个。

2.1 语法
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语法官方介绍

重点翻译:强烈建议使用该方式为Pod构建静态库,文件资源通过键值匹配资源,bundle的名称应该包含Pod的名称来降低名称冲突的可能性。

2.3 resource_bundle探究

下面同样通过是否嵌入xcassets文件来分析这两种情况的优劣。

2.3.1 resource_bundle不嵌入xcassets文件

不嵌入xcassets文件时,和resource一样,直接将文件资源拖入至Assets文件夹下,具体参考上文resource不嵌入xcasset文件中的内容。然后修改podspec文件制定文件资源的方式,如下图所示。

pod install,真机运行,查看资源文件在包中的位置,可以看到一个SCResource_Resources.bundle的文件。查看该文件的大小为21.1M,比原文件略小。

再选择SCResource_Resources.bundle右键显示包内容,可看到我们放进去的Images图片文件夹。

资源文件获取

前面在resource的章节中,我们已经知道需要通过拼接资源的相对路径才能获取相应的资源,所以我们这里也尝试看看会发生什么。

ViewDidLoad方法中键入下面代码:

UIImage *image = [UIImage imageNamed:@"SCResource_Resources.bundle/Images/好运墙/goodluck_smile"];

断点查看是否能正常获取图片资源。

xib中同样这样拼接,真机运行,看能否正常显示图片。

运行后,我们可以如预期一样获取资源文件。

总结:

resource_bundle不嵌入xcasset文件,需拼接文件的相对路径才能正确获取图片资源。

2.3.2 resource_bundle嵌入xcassets文件

嵌入xcassets文件时,也和resource一样,创建xcassets文件,拖入文件资源,并拖入至Assets文件夹下,具体参考上文resource嵌入xcasset文件中的内容。

pod install并真机运行,查看资源文件在包中的位置,我们同样可以看到SCResource_Resources.bundle文件,查看文件大小,可以看到只有16.8M,比前面所有情况都要小。

再选择SCResource_Resources.bundle右键显示包内容,可看到我们放进去的Assets.car文件。与前文的情况一致。

资源文件获取

resource的章节中,如果嵌套xcassets文件,我们可以直接通过图片名称来获取文件资源,那么这里是不是类似呢,我们来试试。

viewDidLoad方法中键入下面代码:

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

运行,查看image对象是否存在。

很遗憾,结果为nil。那么我们拼接路径呢?同样在viewDidLoad方法中键入下面代码:

UIImage *image = [UIImage imageNamed:@"SCResource_Resources.bundle/goodluck_smile"];

运行,查看image对象是否存在。

同样的结果,还是nil

使用这种方式,我们需要换一个方法去获取指定资源,我们需要调用UIImageimageNamed:inBundle: compatibleWithTraitCollection:方法。指定bundle和图片的名称即可。

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];

运行,查看image对象是否存在

运行结果如预期,可获取对应文件资源。

xib中如何设置呢?可以添加分类暴露bundleNameimageName使用IBInspectable修饰,调用imageNamed:inBundle: compatibleWithTraitCollection:方法。

总结:

resource_bundle嵌入xcasset文件,包文件大小相对于其它情况较小,但获取文件资源时,需要封装方法调用UIImageimageNamed:inBundle: compatibleWithTraitCollection:方法。

总结

使用CocoaPods方式组件化,对文件资源进行管理,建议使用resource_bundle/resource_bundles嵌入xcassets文件的方式。这样一来可以使用xcassets的一些特性和优化,也能够在一定程度上减小包的体积。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容