背景介绍
最近推动了最低支持iOS8的事情,并且引入了framework进行部门间合作的机制。整体上,这件事值得做,比以前能少不少麻烦,未来的发展也将更顺畅。不过,跟直接加一个文件夹相比,引入了framework,就像加了一堵墙,有些以前很方便的事情现在也变得麻烦了。比如,资源文件的管理和读取就是其中之一。
是否需要Bundle?
以前的.a静态库是不能包含资源文件的。所以毫无疑问,如果需要xib, img等资源文件的话,需要打包在.bundle中,和.a一起发布,一般保持两者同样的名字。
从iOS8开始,可以用framework,并且可以包含资源文件,可以放弃.bundle文件了。这主要是从使用方便的角度来讲的。将代码和资源分离,完成同一功能要找两个地方,自找麻烦。
在framework中开发,就像跟主程序中开发一样,按照功能分模块,划分文件夹。将同一功能的文件都放在一个文件夹中,包括Storyboard,xib,image,Code等等,以最短路径获取所需要的信息。
将framework和bundle分两个隔离是不合理的,同样,在framework中包含一个bundle同样也是不合理的。模块间隔离的单位是framework,在framework内部,应该考虑充分共享以及获取的方便性,再引入第二级的bundle,只能是自我设限,得不偿失。让.bundle和.a成为历史,全面使用framework。
还有一种情况,就是当资源的体积很大的时候,比如视频、地图之类的。这种时候,将资源单独放在一个bundle中,与程序分离,可以不同时间发布,也不用重新发版本,有一定的意义。不过,对于大资源,直接以文件的形式下载就可以了,有必要用bundle多包一层吗?
除了主程序,其他framework的地位都是平等的,不存在framework之中包含framework的概念。取而代之的,是依赖关系。所以,整体架构在物理上就两层,主程序和framework。逻辑上的层次和包含等概念,都要理解依赖。比如要开发一个Network.framwork,需要用到AFNetworking.framework。在逻辑上是主程序-》Network.framwork-》AFNetworking.framework。但是在物理上的关系是
主程序-》Network.framwork和主程序-》AFNetworking.framework。AFNetworking.framework与Network.framwork地位是完全平等的,只不过想用Network.framwork的程序必须同时将AFNetworking.framework包含进来。可以把framework想象为主程序中的一个文件夹。在没有framework的主程序中,资源是直接放在根目录下的。但是,引入了framework之后,就像把资源移动了相应的子目录。这样路径就发生了变化,NSBundle这个类就是为了区分这种变化。
在framework里面读framwork自己的资源文件
这是framework内部的资源,跟其他都没有关系。但是framework不能单独存在,必须要放在某个“主程序”中才能起作用。bundle参数如果不传,那么默认是mainBundle,这种情况路径就不对了。这种情况下,可以用下面这个API来获得bundle参数。
+ (NSBundle *)bundleForClass:(Class)aClass;
// 获取bundle参数
NSBundle *bundle = [NSBundle bundleForClass:self.class];
// 读UIStoryboard
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@“StoryboardName” bundle:bundle];
// 读UIImage
UIImage *image = [UIImage imageNamed:@"icon_back_gray" inBundle:bundle compatibleWithTraitCollection:nil];
// 文件路径
NSString* htmlPath = [bundle pathForResource:@"index" ofType:@"html"];
在主程序中读framework里面的资源文件
同样也是利用bundle参数来读取,class选择framework中某个导出的class就可以了。
// 获取bundle参数,ZAFinanceFrameworkManager是framework中接口类
NSBundle *bundle = [NSBundle bundleForClass:[ZAFinanceFrameworkManager class]];
在framework中读主程序的资源文件
这个和在主程序中读自己的一样,不需要bundle参数,一定要传的话,就传nil或者[NSBundle mainBundle]
从framework里面读其他framwork里面的资源文件
同样也是利用bundle参数来读取,class选择目标framework中某个导出的class就可以了。
小结
在单体程序中,NSBundle这个参数不需要管,全部传nil或者是默认的[NSBundle mainBundle]
就可以了。
引入了framework之后,就需要NSBundle这个参数来区分资源所在的模块。确定NSBundle比较简单的方法是用下面这个API,其中的class只要选择资源所在的framework中的某个class就可以了。如果是Swift,并且不是类类型,那么就可以用“self.dynamicType”来取得类型。
+ (NSBundle *)bundleForClass:(Class)aClass;
关于模块划分的原则
苹果的指导原则是MVC,在大多数情况是合适的,客户端都比较小,几个页面跳转一下,加几个动画就差不多了。
近来,随着移动设备性能的提升,手机越来越像电脑了,手机客户端也越来越重,慢慢地跟PC端客户端一样。
程序变大了,为了便于维护,就要想办法划分模块,PC端经历过的事情很快就在手机端重演。
从iOS8开始,苹果提供了动态链接库framework,这个就相当于PC端的dll。手机客户端的组件化、平台化也像在PC端那么方便了。